基礎(chǔ)概念
CPU 核心數(shù)和線程數(shù)的關(guān)系
核心數(shù):線程數(shù) = 1:1 ,后面使用了超線程技術(shù)后--》1:2
CPU 時(shí)間片輪轉(zhuǎn)機(jī)制
又稱RR調(diào)度,會(huì)導(dǎo)致上下文切換
什么是進(jìn)程和線程
進(jìn)程:程序運(yùn)行資源分配的最小單元,進(jìn)程內(nèi)部有多個(gè)線程會(huì)共享這個(gè)進(jìn)程。
線程:CPU調(diào)度的最小單位,必須依賴進(jìn)程而存在。
并發(fā)(concurrency)和并行(parallelism)
并行:同一時(shí)刻,可以同時(shí)處理事情的能力。
并發(fā):于單位時(shí)間相關(guān),在單位時(shí)間內(nèi)可以處理事情的能力。
解釋:1.并行是指兩個(gè)或者多個(gè)事件在同一時(shí)刻發(fā)生,而并發(fā)是指兩個(gè)或者多個(gè)事件在同一時(shí)間間隔發(fā)生。
2.并行是在不同實(shí)體上的多個(gè)事件,并發(fā)是在同一個(gè)實(shí)體上的多個(gè)事件。
3.在一臺(tái)處理器上“同時(shí)”處理多個(gè)任務(wù)。在多態(tài)處理器上同時(shí)處理多個(gè)任務(wù)。如 hadoop 分布式集群。
所以并發(fā)編程的目標(biāo)是充分利用處理器上的每一個(gè)核,以達(dá)到最高處理性能。
并發(fā)編程的意義,好處,和注意事項(xiàng)
好處:
1.充分利用cpu 的資源
2:加快用戶響應(yīng)的事件
3:程序模塊化,異步化,可以用多線程解藕。
問(wèn)題:
1:線程共享資源,存在沖突,容易導(dǎo)致死鎖。
2:?jiǎn)?dòng)太多的線程,就會(huì)有搞垮機(jī)器的可能。
認(rèn)識(shí)JAVA里面的線程
java 天生就是多線程的,啟動(dòng)一個(gè)main 就要啟動(dòng)最少5個(gè)線程
上代碼:
/**
* @author sxylml
* @Date : 2019/2/25 15:35
* @Description: java 線程,就算只啟動(dòng)一個(gè)main線程,JVM 也會(huì)啟動(dòng)其它5-6個(gè)線程
*/
public class OnlyMain {
public static void main(String[] args) {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for (int i = 0; i < threadInfos.length; i++) {
System.out.println(threadInfos[i].getThreadId()+"-"+ threadInfos[i].getThreadName());
}
}
}
運(yùn)行結(jié)果:

Attach Listener :線程是負(fù)責(zé)接收到外部的命令,而對(duì)該命令進(jìn)行執(zhí)行的并且吧結(jié)果返回給發(fā)送者。通常我們會(huì)用一些命令去要求jvm給我們一些反饋信息,如:java -version、jmap、jstack等等。如果該線程在jvm啟動(dòng)的時(shí)候沒(méi)有初始化,那么,則會(huì)在用戶第一次執(zhí)行jvm命令時(shí),得到啟動(dòng)。
signal dispather: 前面我們提到第一個(gè)Attach Listener線程的職責(zé)是接收外部jvm命令,當(dāng)命令接收成功后,會(huì)交給signal dispather線程去進(jìn)行分發(fā)到各個(gè)不同的模塊處理命令,并且返回處理結(jié)果。signal dispather線程也是在第一次接收外部jvm命令時(shí),進(jìn)行初始化工作。
Finalizer: 用來(lái)執(zhí)行所有用戶Finalizer 方法的線程
Reference Handler :它主要用于處理引用對(duì)象本身(軟引用、弱引用、虛引用)的垃圾回收問(wèn)題。
JAVA 新啟動(dòng)線程的三種方式:
package com.cap1.tools;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author sxylml
* @Date : 2019/2/25 15:42
* @Description: 創(chuàng)建線程的三種方式DEMO
*/
public class NewThread {
public static void main(String[] args) throws ExecutionException, InterruptedException {
InteriorThread useThread = new InteriorThread();
useThread.setName("staticInteriorThread");
useThread.start();
OuterUseThread outerThread = new OuterUseThread();
outerThread.setName("OuterUseThread");
outerThread.start();
// 繼承runable 接口
new Thread(new UseRunnable()).start();
//
UseCallable useCallable = new UseCallable();
FutureTask futureTask = new FutureTask(useCallable);
new Thread(futureTask).start();
// 可以輸出返回參數(shù)
System.out.println(futureTask.get());
}
/**
* 1:繼承Thread 類
* 因?yàn)榉庆o態(tài)內(nèi)部類對(duì) main 方法而言是不直接可見(jiàn)的
*/
private static class InteriorThread extends Thread {
@Override
public void run() {
System.out.println("線程信息 ID:[ "+Thread.currentThread().getId()+"] name: ["+Thread.currentThread().getName()+ "] I'm extends Thread");
}
}
/**
* 2:實(shí)現(xiàn)Runnable 接口
*/
private static class UseRunnable implements Runnable {
@Override
public void run() {
System.out.println("線程信息 ID:[ "+Thread.currentThread().getId()+"] name: ["+Thread.currentThread().getName()+ "] I'm implements Runnable");
}
}
/**
* 3 實(shí)現(xiàn) Callable 接口是允許有返回類型的,類型就是接口《泛型》
*/
private static class UseCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("線程信息 ID:[ "+Thread.currentThread().getId()+"] name: ["+Thread.currentThread().getName()+ "] I'm implements Callable");
return "CallableResult";
}
}
}
class OuterUseThread extends Thread {
@Override
public void run() {
System.out.println("線程信息 ID:[ "+Thread.currentThread().getId()+"] name: ["+Thread.currentThread().getName()+ "] I'm extends Thread");
}
}
運(yùn)行結(jié)果:

JAVA線程的生命周期:

新建狀態(tài)(New):當(dāng)線程對(duì)象對(duì)創(chuàng)建后,即進(jìn)入了新建狀態(tài),如: InteriorThread t = new InteriorThread();
就緒狀態(tài)(Runnable):當(dāng)調(diào)用線程對(duì)象的start()方法(t.start();),線程即進(jìn)入就緒狀態(tài)。處于就緒狀態(tài)的線程,只是說(shuō)明此線程已經(jīng)做好了準(zhǔn)備,隨時(shí)等待CPU調(diào)度執(zhí)行,并不是說(shuō)執(zhí)行了t.start()此線程立即就會(huì)執(zhí)行;
運(yùn)行狀態(tài)(Running):當(dāng)CPU開(kāi)始調(diào)度處于就緒狀態(tài)的線程時(shí),此時(shí)線程才得以真正執(zhí)行,即進(jìn)入到運(yùn)行狀態(tài)。注:就 緒狀態(tài)是進(jìn)入到運(yùn)行狀態(tài)的唯一入口,也就是說(shuō),線程要想進(jìn)入運(yùn)行狀態(tài)執(zhí)行,首先必須處于就緒狀態(tài)中;
阻塞狀態(tài)(Blocked):處于運(yùn)行狀態(tài)中的線程由于某種原因,暫時(shí)放棄對(duì)CPU的使用權(quán),停止執(zhí)行,此時(shí)進(jìn)入阻塞狀態(tài),直到其進(jìn)入到就緒狀態(tài),才 有機(jī)會(huì)再次被CPU調(diào)用以進(jìn)入到運(yùn)行狀態(tài)。根據(jù)阻塞產(chǎn)生的原因不同,阻塞狀態(tài)又可以分為三種:
1.等待阻塞:運(yùn)行狀態(tài)中的線程執(zhí)行wait()方法,使本線程進(jìn)入到等待阻塞狀態(tài);
2.同步阻塞 -- 線程在獲取synchronized同步鎖失敗(因?yàn)殒i被其它線程所占用),它會(huì)進(jìn)入同步阻塞狀態(tài);
3.其他阻塞 -- 通過(guò)調(diào)用線程的sleep()或join()或發(fā)出了I/O請(qǐng)求時(shí),線程會(huì)進(jìn)入到阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)、或者I/O處理完畢時(shí),線程重新轉(zhuǎn)入就緒狀態(tài)。
死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期。
線程的 run,start 方法
/**
* @author sxylml
* @Date : 2019/2/25 16:27
* @Description:
*/
public class StartAndRun {
private static class RunThread extends Thread {
@Override
public void run() {
int i = 5;
while (i > 0) {
// 休眠一千毫秒
SleepTools.ms(100);
System.out.println("i'm " + Thread.currentThread().getName() + " i=" + i--);
}
}
}
public static void main(String[] args) {
Thread beCalled = new RunThread();
beCalled.setName("beCalled");
beCalled.run(); // 面向?qū)ο?,這樣直接調(diào)用run 和普通類一樣 : run 歸誰(shuí)調(diào)用,就歸那個(gè)線程,這樣運(yùn)行結(jié)果就是main
beCalled.start();// 只有調(diào)用start() ,才會(huì)是線程
}
}

守護(hù)線程:
public class DaemonThread {
private static class UseThread extends Thread {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
try {
while (!isInterrupted()) {
System.out.println(threadName + " extend Thread");
// 拋出異常
Thread.sleep(100);
}
// 拋出異常這代碼就不執(zhí)行咯
System.out.println(threadName + " interrupted " + isInterrupted());
} catch (Exception e) {
e.printStackTrace();
System.out.println(threadName + " try interrupted " + isInterrupted());
Thread.currentThread().interrupt();
} finally {
// 是守護(hù)線程的的情況,不一定執(zhí)行
System.out.println("-------------finally -----------------");
}
}
}
public static void main(String[] args) throws InterruptedException {
UseThread useThread = new UseThread();
// 守護(hù)線程 和主線程共死,finally不能保證一定執(zhí)行
useThread.setDaemon(true);
useThread.start();
Thread.sleep(1000);
// 發(fā)送中斷通知
useThread.interrupt();
}
}
如何正確的終止線程
1:線程自然終止:自然執(zhí)行完畢或拋出未處理異常
stop(),resume(),supende() 已經(jīng)不建議使用,stop()會(huì)導(dǎo)致線程不會(huì)正確釋放資源,suspend()容易導(dǎo)致死鎖。
2:調(diào)用interrupt() 方法中斷一個(gè)線程,并不是強(qiáng)行關(guān)閉這個(gè)線程,只是跟這個(gè)線程打個(gè)招呼,講線程的中斷標(biāo)志設(shè)置為true,線程是否中斷,由線程本身決定。
isInterrupted() 判斷當(dāng)前線程是否處于中斷狀態(tài)。
static 方法interrupted() 判定當(dāng)前線程是否處于中斷狀態(tài),同時(shí)中斷標(biāo)志位改為false.
方法如果拋出interruptedException ,線程的中斷標(biāo)志位會(huì)被復(fù)位或成false ,如果確實(shí)是需要中斷線程,要求我們自己在catch 語(yǔ)句塊里再次調(diào)用interrupt().
/**
* @author sxylml
* @Date : 2019/2/25 15:52
* @Description: 如何正確的中斷線程
*/
public class EndThread {
private static class UseThread extends Thread {
public UseThread(String name) {
super(name);
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
// 不是中斷的時(shí)候,就循環(huán)執(zhí)行
while (!isInterrupted()) {
// 當(dāng)沒(méi)有向線程發(fā)送中斷請(qǐng)求,這執(zhí)行循環(huán)里面的值
System.out.println(threadName + " is run in while ! in interrupt flag is " + this.isInterrupted());
}
// 當(dāng)發(fā)送了中斷請(qǐng)求,并且中斷標(biāo)識(shí)改變了,則退出循環(huán)
System.out.println(threadName + " is run out while ! in interrupt flag is " + this.isInterrupted());
}
}
public static void main(String[] args) {
Thread endThread = new UseThread("endThread");
endThread.start();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("向線程發(fā)送中斷請(qǐng)求!");
endThread.interrupt();
}
}

public class EndRunnable {
private static class UseRunable implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
while (!Thread.currentThread().isInterrupted()) {
// 當(dāng)沒(méi)有向線程發(fā)送中斷請(qǐng)求,這執(zhí)行循環(huán)里面的值
System.out.println(threadName + " is running in while ! interrupt flag is " + Thread.currentThread().isInterrupted());
}
// 當(dāng)發(fā)送了中斷請(qǐng)求,并且中斷標(biāo)識(shí)改變了,則退出循環(huán)
System.out.println(threadName + " is run out while ! interrupt flag is " + Thread.currentThread().isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
UseRunable useRunable = new UseRunable();
Thread thread = new Thread(useRunable, "endRunnable");
thread.start();
Thread.sleep(20);
System.out.println("發(fā)起中斷請(qǐng)求!");
thread.interrupt(); // 發(fā)起中斷信號(hào)
}
}
