多執行緒學習筆記
一、執行緒
1、定義執行緒有兩種方式:擴充套件Thread類和實現Runnable介面。使用start方法啟動執行緒。
2、執行緒狀態:建立(new),就緒(Runnable),執行(Running),阻塞(Blocked)和終止(dead),狀態轉換如下圖。
3、sleep和wait的區別
1)sleep是Thread類的方法,wait是Object類的方法 2)Thread.sleep不會導致鎖行為的改變, 如果當前執行緒是擁有鎖的, 那麼Thread.sleep不會讓執行緒釋放鎖 3)Thread.sleep和Object.wait都會暫停當前的執行緒. OS會將執行時間分配給其它執行緒. 區別是, 呼叫wait後, 需要別的執行緒執行notify/notifyAll才能夠重新獲得CPU執行時間。
4、sleep和yield方法區別 1) sleep方法給其他執行緒執行機會時不會考慮執行緒的優先順序,因此低優先順序的執行緒也有機會執行。yield方法只會給相同優先順序或更高優先順序的執行緒以執行的機會 2)執行緒執行sleep方法後轉入阻塞狀態,而yield方法執行後轉入就緒(ready)狀態 3)sleep方法宣告丟擲InterruptException,而yield方法沒有宣告任何異常 4) sleep方法比yield方法具有更好的移植性
5、常用方法
start(): 使執行緒開始執行;java虛擬機器呼叫該執行緒的run方法 run():執行業務方法 setName():改變執行緒名稱,使之與引數name相同 setPriority():更改執行緒的優先順序 setDaemon(boolean):將改執行緒標記為守護執行緒或使用者執行緒;守護執行緒是在後臺執行並且不會阻止JVM終止的執行緒,當沒有使用者執行緒在執行的時候,JVM關閉程式並且退出。 join(long milliesec):等待執行緒終止的時間最長為millis毫秒,當前執行緒A等待thread執行緒終止之後才從thread.join()返回。 interrupt():中斷執行緒 isAlive():測試執行緒是否處於活動狀態 yield():暫停當前正在執行的執行緒物件,並執行其他執行緒,放棄時間不確定,不會讓出鎖 sleep():指定的毫秒內讓正在執行的執行緒休眠 currentThread():返回對當前正在執行的執行緒物件的引用
6、並行:多個cpu例項或者多臺機器同時執行一段處理邏輯,是真正的同時。
併發:通過cpu排程演算法,讓使用者看上去是同步執行。 悲觀鎖 synchronized:假定會發生併發衝突,遮蔽一切可能違反資料完整性的操作 樂觀鎖:假設不會發生併發衝突,只在提交操作時檢查是否違反資料完整性。如CAS atomic;缺點是不能解決髒讀問題,會出現ABA問題,新增版本號來避免此問題
二、執行緒間通訊方式
1、管道:半雙工,資料只能單向流動,有血緣關係的程序間流動
2、有名管道: 允許無血緣關係的程序間流動
3、訊號量:訊號量是一個計數器,可以用來控制多個程序對共享資源的訪問。是一種同步手段
4、訊息佇列:由訊息組成的連結串列,存放在核心中,並由訊息佇列識別符號標識
5、訊號: 用於通知接收程序某一事件已經發生。
6、共享記憶體
7、套接字socket 長連線:整個通訊過程,客戶端和服務端只用一個Socket物件,長期保持Socket的連線 短連線:每次請求,都新建一個Socket,處理完一個請求就直接關閉掉Socket
三、執行緒間通訊機制
有鎖機制、訊號量機制和訊號機制
鎖機制: 互斥鎖: 提供了以排它方式阻止資料結構被併發修改的方法 讀寫鎖:允許多個執行緒同時讀共享資料,而對寫操作互斥 條件變數:可以以原子阻塞程序,直到某個特定條件為真為止
訊號量機制:包括無名執行緒訊號量與有名執行緒訊號量
四、執行緒安全的實現方法
1、互斥同步:臨界區、互斥量、訊號量
2、非阻塞同步: 硬體指令集
3、ReentrantLock:可重入鎖 1)等待可中斷:持有鎖的執行緒長期不釋放鎖的時候,正在等待的執行緒可以選擇放棄等待,改為處理其他事情 2)可實現公平鎖:公平鎖是指多個執行緒在等待同一個鎖時,必須按照申請鎖的時間順序來依次獲得鎖; 3)鎖可以繫結多個條件 4)預設非公平的通過帶布林值的建構函式要求使用公平鎖
4、synchronized 重量級操作,狀態切換需要耗費很多的處理器時間。確實必要才使用,虛擬機器本身對它的優化:通知作業系統阻塞執行緒之前加入一段自旋等待過程,避免頻繁的切入到核心狀態之中。是非公平的。
5、如何確保執行緒安全
1)使用原子類 2)實現併發鎖 3)使用volitale關鍵字 4)使用不變類和執行緒安全類
五、Web併發訪問問題
1、Servlet執行緒安全問題:單例模式,存線上程安全問題 1)在Servlet中不要定義類變數或例項變數 2)為操作該變數的所有方法加synchronized修飾符,進行同步控制
2、Spring執行緒安全問題:優點是我們不用每次建立Controller, 減少了物件建立和垃圾收集的時間,缺點是併發訪問時會出現安全問題。 1)避免在Controller中定義類變數和例項變數 2)在Controller中使用ThreadLocal變數 3)宣告scope="prototype",每次建立新的Controller
3、SpringMVC執行緒安全問題 1)對操作共享變數的所有方法進行同步控制 2)同步共享變數如:Collections.synchronizedMap() 3)使用同步物件
六、執行緒池 1、為什麼使用執行緒池 1)建立/銷燬執行緒伴隨著系統開銷,過於頻繁,影響處理效率 2)執行緒併發數量過多,搶佔系統資源導致阻塞 3)對執行緒進行一些簡單的管理(延時,定時迴圈等) 2、優點 1)降低資源消耗 2)提高響應速度 3)提高執行緒的可管理性 3、java.uitl.concurrent.ThreadPoolExecutor 1)corePoolSize:核心執行緒數最大值 2)maximunPoolSize:執行緒池最大執行緒數(核心執行緒 + 非核心執行緒) 3)keepAliveTime:對非核心執行緒沒有任務執行時最多保持多久時間會終止 4)workQueue:一個阻塞佇列,用來儲存等待執行的任務 ArrayBlockingQueue:基於陣列的先進先出佇列,此佇列建立時必須指定大小; LinkedBlockQueue:基於連結串列的先進先出佇列,如果建立時沒有指定此佇列大小,則預設為Integer.MAX_VALUE; SynchronousQueue:這個佇列比較特殊,它不會儲存提交的任務,而是將直接新建一個執行緒來執行新來的任務 5)threadFactory:執行緒工廠 6)handler:表示當拒絕處理任務時的策略 AbortPolicy:丟棄任務並丟擲異常 DiscardPolicy:丟棄任務不丟擲異常 DiscardOldestPolicy:丟棄佇列最前面的任務,重新嘗試執行任務 由呼叫執行緒處理該任務 7)策略 執行緒數量未達到corePoolSize,則新建一個核心執行緒執行任務 執行緒數量達到了corePoolSize,則進入佇列等待 佇列已滿,新建非核心執行緒執行任務 佇列已滿,匯流排程達到了maximumPoolSize則丟擲RejectedExcutionHandler異常 4、執行緒池分類 1)fixThreadPool:正規執行緒 有指定的執行緒數的執行緒池,有核心的執行緒,裡面有固定的執行緒數量,響應的速度快 沒有超時機制,佇列大小沒有限制,除非執行緒池關閉了核心執行緒才會回收 2)cacheThreadPool:快取執行緒池 只有非核心執行緒,最大執行緒數很大有超時回收機制 沒有考慮到系統的實際記憶體大小 3)singleThreadPool:單執行緒執行緒池 只有一個核心執行緒,不處理併發的操作,不會被回收 4)ScheduledThreadPool 有延遲執行和週期重複執行的執行緒池,核心執行緒池固定,非核心執行緒池不限,閒置時會回收。 5、可能出現的危險 死鎖、資源不足、併發錯誤、執行緒洩漏、請求過載、
6、核心執行緒 核心執行緒預設情況下會一直存活線上程池中,即使這個核心執行緒啥也不幹(閒置狀態)。 執行緒池新建執行緒的時候,如果當前執行緒總數小於corePoolSize,則新建的是核心執行緒,如果超過corePoolSize,則新建的是非核心執行緒;allowCoreThreadTimeOut設定此項可以讓核心執行緒閒置狀態下回收 7、新增任務 ThreadPoolExecutor.execute(Runnable command) 8、執行緒池的關閉 shutdown():不會立即關閉,是等待所有任務快取佇列中的任務都執行完後才終止,但也不會接受新的執行緒 shutdownNow():立即終止執行緒池,嘗試打斷正在執行的任務,並且清空任務快取佇列,返回尚未執行的任務