已知線程有五大狀態
(圖來自:https://blog.csdn.net/u012403290/article/details/64910926?locationNum=11&fps=1)
新建狀態:新建線程對象,並沒有調用start()方法之前
就緒狀態:調用start()方法之後線程就進入就緒狀態,但是並不是說只要調用start()方法線程就馬上變爲當前線程,在變爲當前線程之前都是爲就緒狀態。值得一提的是,線程在睡眠和掛起中恢復的時候也會進入就緒狀態哦。
運行狀態:線程被設置爲當前線程,開始執行run()方法。就是線程進入運行狀態
阻塞狀態:線程被暫停,比如說調用sleep()方法後線程就進入阻塞狀態
死亡狀態:線程執行結束
問1:線程類的業務邏輯一般都在run方法中,爲什麼要調用start方法而不是run方法運行線程呢,運行run方法可以嗎?
答:run方法和start方法都可以運行寫在run方法體類的邏輯代碼,但是調用run方法是當前線程(例如main方法)去執行這段邏輯代碼,相當於還是一個人,調用run()後去做另外一件事情了,另外一件事情做完後,又回來繼續做(main方法)這邊的事情。而調用start()纔是多線程的精髓。
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
上面是Thread類的start()源碼,可以看到這裏先將當前線程加入到線程的group中(默認線程組,所有的線程都有線程組)。然後調用了start0(),這裏的start0沒有java代碼。
在一位大神(https://www.linuxidc.com/Linux/2016-03/128997.htm)的博文中找到對start0的解釋,這裏應該是調用了java底層的natives方法,最後應該是調用了一個叫做JVM_StartThread的方法,按照名字來說應該讓jvm去開啓一個線程。所以start()做了什麼已經很明顯了。它調用了虛擬機,開啓一個新的線程,去執行run。也就是,當前線程執行start()相當於,找來一個人,告訴他去做run()裏面的事情,然後馬上恢復到(main())這邊的工作中,如果main中沒有更多的邏輯,他就可以下班啦。
2.線程池有什麼用,可以怎麼用?
答:線程池與各種連接池一樣,目的在是在於減少虛擬機頻繁創建、銷燬線程的性能損耗,需要線程,不需要創建,從線程池內拿,用完再扔回去,由線程池維護。
/**
* 線程池
*/
protected ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
threadPool.submit(thread);
最簡單線程池代碼創建和使用代碼線程池維護線程數由系統核心數決定,關於各種線程池的區別的文章網上有很多,大家可以按照自己閱讀理解習慣找來看。
但是在最新的阿里巴巴java開發手冊中,明確標明瞭,不要用Executors創建線程池,在對性能要求比較高的系統中,還是不要使用Executors簡單創建線程池的方法。
【強制】線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。
說明:Executors 返回的線程池對象的弊端如下:
1)FixedThreadPool和 SingleThreadPool:
允許的請求隊列長度爲 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。
2)CachedThreadPool和 ScheduledThreadPool:
允許的創建線程數量爲 Integer.MAX_VALUE,可能會創建大量的線程,從而導致 OOM。
ThreadPoolExecutor的幾種構造方法(利用Executors創建線程池其實也是調用了這邊的方法):