為什麼使用多執行緒
使用多執行緒,可以把一些大任務分解成多個小任務來執行,多個小任務之間互不影像,同時進行,這樣,充分利用了cpu資源。
實現多執行緒的兩種方式
- 繼承Thread類,重寫run方法;
- 實現Runable介面,實現run方法;
由於JAVA只支援單繼承,在繼承了其他類的基礎上再想實現多執行緒就只能用實現Runable介面了,所以更加推薦第二種方法
執行緒的狀態
新建,就緒,執行,阻塞,死亡
執行緒池的作用
(1)降低資源消耗,可重複利用
(2)加快響應速度,不需要重複建立
(3)便於管理
spring 中的執行緒池
Spring中的執行緒池是由ThreadPoolTaskExecutor類來實現的
Java程式碼初始化:
private void test2(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(15);
executor.setKeepAliveSeconds(1);
executor.setQueueCapacity(5);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
executor.execute(new Runnable(){
@Override
public void run() {
//執行的程式碼
}
});
}
複製程式碼
執行緒通訊
通訊方式
- 等待通知機制:兩個執行緒通過對同一物件呼叫等待wait()和通知notify()方法來進行通訊案例:兩個執行緒交替列印奇偶數 深入理解執行緒通訊
- join()方法:其實也是呼叫等待通知機制
- volatile共享記憶體:java採用共享記憶體的方式進行同信(也可以使用管道)
- 執行緒響應中斷
- 執行緒池的awaitTermination() 方法
- 管道通訊
volatile關鍵字
典型案例:i++和++i不是原子操作
作用
- 保證記憶體可見性
- 防止 JVM 進行指令重排優化
解釋
cpu的速度往往與記憶體的速度很難匹配,所以在cpu和記憶體之間加入快取記憶體(每個執行緒都有自己的快取記憶體),在cpu沒有命中快取記憶體的時候才會去記憶體中取,在併發程式設計中就會出現快取記憶體沒有同步到記憶體中的情況。
用volatile修飾的變數會強制任何執行緒對此變數的操作都會立即重新整理到主記憶體中,並且強制讓快取了此變數的棧清空,必須從主記憶體中獲取資料,保證了一致性(注意,修飾之後並不是讓執行緒從主記憶體中取,依然將變數拷貝到快取區)。所以可以保證取出的值一定是最新的。
注意
volatile並不保證原子性,不保證執行緒安全性
更多請看:volatile關鍵字詳解