多线程—synchronized及同步器
synchronized關鍵字
synchronized有兩種類型的鎖:
- 類鎖:synchronized修飾靜態方法、synchronized(T.class)給代碼塊上鎖。
- 對象鎖:除了類鎖,所有其他的上鎖方式都認為是對象鎖。synchronized修飾普通方法、synchronized(this)給代碼塊上鎖等
規則:
- 加了相同鎖的東西,任一時刻只能有一個線程持有鎖,其他線程等待。
- 加了不同鎖的東西訪問互相不干擾 。
判斷:
- 不同類型的鎖不是同一把鎖。
- 加的是對象鎖,那么必須是同一個對象實例才是同一把鎖 。
- 加的是類鎖,那必須是同一類才是同一把鎖。
線程阻塞與喚醒方法:
? ? 1. sleep() 方法? ? ?不釋放鎖
sleep(毫秒),指定以毫秒為單位的時間,使線程在該時間內進入線程阻塞狀態,期間得不到cpu的時間片,等到時間過去了,線程重新進入可執行狀態。不會釋放資源。(暫停線程,不會釋放鎖)
2.suspend() 和 resume() 方法
掛起和喚醒線程,suspend()使線程進入阻塞狀態,只有對應的resume()被調用的時候,線程才會進入可執行狀態。(不建議用,容易發生死鎖)
3. yield() 方法? ? ?不釋放鎖
會使的線程放棄當前分得的cpu時間片,但此時線程任然處于可執行狀態,隨時可以再次分得cpu時間片。yield()方法只能使同優先級的線程有執行的機會。調用 yield()的效果等價于調度程序認為該線程已執行了足夠的時間從而轉到另一個線程。(暫停當前正在執行的線程,并執行其他線程,且讓出的時間不可知)
4.wait() 和 notify() 方法? ? ?釋放鎖
兩個方法搭配使用,wait()使線程進入阻塞狀態,調用notify()時,線程進入可執行狀態。wait()內可加或不加參數,加參數時是以毫秒為單位,當到了指定時間或調用notify()方法時,進入可執行狀態。(屬于Object類,而不屬于Thread類,wait()會先釋放鎖住的對象,然后再執行等待的動作。由于wait()所等待的對象必須先鎖住,因此,它只能用在同步化程序段或者同步化方法內,否則,會拋出異常IllegalMonitorStateException.)
5.join()方法? ? ?釋放鎖
也叫線程加入。是當前線程A調用另一個線程B的join()方法,當前線程轉A入阻塞狀態,直到線程B運行結束,線程A才由阻塞狀態轉為可執行狀態。
6.await()和signal()? ? ?釋放鎖
ConditionObject方法,實現線程掛起和喚醒。
sleep和wait的區別有:
如果線程A希望立即結束線程B,則可以對線程B對應的Thread實例調用interrupt方法。如果此刻線程B正在wait/sleep/join,則線程B會立刻拋出InterruptedException,在catch() {} 中直接return即可安全地結束線程。
需要注意的是,InterruptedException是線程自己從內部拋出的,并不是interrupt()方法拋出的。對某一線程調用interrupt()時,如果該線程正在執行普通的代碼,那么該線程根本就不會拋出InterruptedException。但是,一旦該線程進入到wait()/sleep()/join()后,就會立刻拋出InterruptedException。
同步器(Synchronizer)
Semaphore:
可以控制同時訪問資源的線程個數
public Semaphore(int permits) //permits線程數void acquire() //從信號量獲取一個許可,如果無可用許可前將一直阻塞等待,void acquire(int permits) //獲取指定數目的許可,如果無可用許可前也將會一直阻塞等待boolean tryAcquire() //從信號量嘗試獲取一個許可,如果無可用許可,直接返回false,不會阻塞 boolean tryAcquire(int permits) boolean tryAcquire(int permits, long timeout, TimeUnit unit)void release() //釋放一個許可,在finally中使用,注意:多次調用該方法,會使信號量的許可數增加,達到動態擴展的效果,如:初始permits為1, 調用了兩次release,最大許可會改變為2int availablePermits() //獲取當前信號量可用的許可CountDownLatch:
使一個線程等待其他線程各自執行完畢后再執行。是通過一個計數器來實現的,計數器的初始值是線程的數量。每當一個線程執行完畢后,計數器的值就-1,當計數器的值為0時,表示所有線程都執行完畢,然后在閉鎖上等待的線程就可以恢復工作了。
public CountDownLatch(int count) //參數count為計數值public void await() throws InterruptedException //調用該方法的線程會進入阻塞狀態,直到count值為0才繼續執行//和await()類似,只不過等待一定的時間后count值還沒變為0的話就會繼續執行 public boolean await(long timeout, TimeUnit unit) throws InterruptedExceptionpublic void countDown(); //將CountDownLatch對象count值(初始化時作為參數傳入構造方法)減1Exchanger:
一個線程在完成一定的事務后想與另一個線程交換數據,則第一個先拿出數據的線程會一直等待第二個線程,直到第二個線程拿著數據到來時才能彼此交換對應數據。
Exchanger<T> exchanger = new Exchanger<T>();//等待另一個線程到達此交換點(除非當前線程被中斷),然后將給定的對象傳送給該線程,并接收該線程的對象,否則阻塞。 data = exchanger.exchange(T);//等待另一個線程到達此交換點(除非當前線程被中斷或超出了指定的等待時間),然后將給定的對象傳送給該線程,并接收該線程的對象。 exchange(T t, long timeout, TimeUnit unit):CyclicBarrier:
它允許線程集等待直至其中預定數目的線程到達某個狀態(這個狀態叫公共障柵(barrier)),然后可以選擇執行一個處理障柵的動作。適用場景:當多個線程都完成某操作,這些線程才能繼續執行時,或都完成了某操作后才能執行指定任務時。對CyclicBarrier對象調用await方法即可讓相應線程進入barrier狀態,等到預定數目的線程都進入了barrier狀態后,這些線程就可以繼續往下執行了
//構造函數,parties 是參與線程的個數,Runnable 參數,最后一個到達線程要做的任務。 CyclicBarrier(int parties) CyclicBarrier(int parties, Runnable barrierAction)//線程調用await()表示到達柵欄,Exception 表示柵欄已經被破壞,可能是其中一個線程await()時被中斷或者超時 int await() throws InterruptedException, BrokenBarrierException int await(long timeout, TimeUnit unit) throws InterruptedException,BrokenBarrierException, TimeoutException?
?
?
?
?
?
?
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的多线程—synchronized及同步器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 26条安全开车经验 开车20年老司机分享
- 下一篇: 多线程—生产者消费者模式、银行家算法