多線程編程核心技術總結(讀周志明書籍的總結)


多線程編程核心技術總結

 

1.Java多線程基本技能

1.1進程和線程的概念:

進程是獨立的程序,線程是在進程中獨立運行的子任務。

1.2使用多線程

1.2.1實現方法:繼承Thread類,重寫Runnable接口。

1.2.2線程安全問題:並發修改公共的實例變量,i++,i--

1.3線程Thread類的一些方法:

currentThread() 放回代碼段正在被那個線程調用

isAlive() 判斷線程是否處於活動狀態

sleep() 使得當前線程退出CPU片段,等待獲取鎖

1.4停止線程

1.4.1使用interrupt()方法不是使線程馬上停止執行,而是打了個暫停的標記。

this.interrupted():測試當前線程是否已經中斷,this.isInterrupted():測試當前線程是否已經中斷。

1.4.2停止線程常用方法--異常法(拋出異常)

1.4.3 在sleep()時,去interrupted。

1.4.4 yield()方法的作用是放棄當前的CPU資源,放棄多長時間不確定,立即加入到CPU的競爭中。

1.5線程優先級

優先級從1—10不等,設置用setPriority()方法,但是優先級具有隨機性,優先級高的不一定先執行。

1.6守護線程

Java線程有兩種,一種是守護線程(Daemon)另一是用戶線程,垃圾回收線程就是典型的守護線程。守護線程為其它線程的運行提供便利,當其他線程都結束時,守護線程才與JVM一同結束工作。

 

2.對象及變量的並發訪問

線程安全問題出現在“實例變量”中,如果是方法內部的私有變量,則不存在線程安全問題。

2.1synchronized同步方法

(1)Synchronized獲得對象鎖,那個線程先執行帶synchronized關鍵字的方法;那個線程就持有該方法所屬對象的鎖,其它訪問同一對象的線程就只能等待;

(2)只有共享資源的讀寫才需要做同步化;

(3)A線程先持有object對象的鎖,B線程可以以異步的方式調用object對象的非synchronized方法。

A線程先持有object對象的鎖,B線程如果調用object對象的synchronized方法,則需要等待,即同步。

(4) synchronized鎖重入:synchronized方法內部調用本類其它的synchronized方法時,是永遠可以得到鎖的。

(5) 出現異常,鎖自動釋放。同步不具有繼承性。

2.2 synchronized同步語句塊

Synchronized方法是對當前對象進行加鎖,而synchronized代碼塊是對某一個對象加鎖。Synchronized方法效率太低,synchronized代碼塊更加細粒化

(1)synchronized同步方法

1)對其他同一對象的synchronized方法或synchronized(this)同步代碼塊呈阻塞狀態。

2)同一時間只有一個線程可以執行synchronized同步方法中的代碼

(2)synchronized(this)同步代碼塊

1)對其他同一對象的synchronized方法或synchronized(this)同步代碼塊呈阻塞狀態。

2)同一時間只有一個線程可以執行synchronized(this)同步代碼塊中的代碼

(3)synchronized(對象x)同步代碼塊

1)在多個線程持有“對象監視器”為同一個對象的前提下,同一時間只有一個線程可以執行synchronized(對象x)同步代碼塊;

2)持有“對象監視器”為同一個對象的前提下,同一時間只有一個線程可以執行synchronized(對象x)同步代碼塊。

對象監視器必須是同一個對象才是同步的,否則是異步調用。

 

(4)靜態同步Synchronized方法與Synchronized(class)代碼塊

Synchronized關鍵字用於static方法上時,如果這樣寫,那是對當前類進行加鎖與Synchronized(class)代碼塊作用一致。Synchronized關鍵字用於非static方法上時,如果這樣寫,那是對當前類的對象進行加鎖。

 

(5)多線程死鎖:比如兩個對象持有對象鎖,在同步方法(或者塊)內部同時請求另一個對象的對象鎖。

(6)只要對象不變,即使對象屬性被改變,運行的結果還是同步的。

2.3 volatile修飾屬性

(1) volatile強制從公共堆棧中取得變量的值,而不是從線程私有數據棧取得變量值。

(2) volatile與synchronized關鍵字比較

1)volatile是線程同步的輕量級實現,其性能肯定比synchronized要好,並且volatile只能修飾變量,而synchronized可修飾方法、代碼塊。

2)多線程訪問volatile不會發生阻塞,而synchronized會出現阻塞。

3) volatile保證數據的可見性,不保證原子性;而synchronized既可以保證原子性也間接保證可見性。

4) volatile解決變量在多線程之間的可見性,而synchronized解決的是多線程之間訪問資源的同步性。線程安全包括原子性和可見性。

 

線程工作內存中: read  (load  use asign) store write 括號中的三步非原子性。

 

(3)synchronized可以保證同一時刻,只有一個線程可以執行某一個方法或某一個代碼塊。包括兩個特征:互斥性和可見性。同步synchronized不僅可以解決一個線程看到對象處於不一致狀態,還可以保證進入同步方法或者同步代碼塊的每個線程,都看到由同一個鎖保護之前所有的修改效果。

3.線程間通信

3.1等待通知機制的實現

(1)Wait()/notify()/notifyAll()都是Object類的方法。調用這些方法之前需先獲得對象鎖。調用wait()方法的線程將會在該代碼行處停止且釋放鎖,進入“預執行隊列”被喚醒之后才有機會獲取鎖。執行Notify()方法的線程不會釋放鎖,隨機喚醒一個正在等待“共享資源”的線程,如果一個阻塞的共享資源的線程都沒有,則忽略。notifyAll()喚醒全部的,等本線程退出CPU,這些線程去爭搶鎖。

(2)每個鎖對象都有兩個隊列,一個是就緒隊列(可運行),一個是阻塞隊列(要喚醒之后才是可運行)。

(3)sleep方法不釋放鎖。遇到異常會釋放鎖。

(4)wait(long)等待long時間,如果沒有其他線程來喚醒它,自動喚醒。使用wait和notify時要注意wait條件發生變化,容易造成程序邏輯混亂。

(5)生產者、消費者模式實現:生產者中Value值不為空,則lock.wait();否則進行設置值的操作,然后lock.notify()喚醒消費者。消費者中value為空,則lock.wait(),否則進行取值操作,然后lock.notify()喚醒生產者。

多生產-單消費:使用notify會造成假死,使用notifyAll來解決。

(6)通過管道流來進行線程間通信,PipedInputStream,PipedOutputStream,PipedReader和PipedWriter。最后outputStream.connect(inputStream)

3.2方法join的使用

(1)方法join是使所屬的線程對象x執行run方法的任務,而使當前線程z進行無限期的阻塞,等待線程銷毀后在繼續執行z后面的代碼。Join過程中,如果當前線程被中斷,則出現異常。

(2)join(long)內部是使用wait來實現的,所以會釋放鎖。Sleep卻不會。

3.3類ThreadLocal的使用

(1)這個類解決每個線程都有自己的共享變量的問題。

ThreadLocal t=new ThreadLocal(); 這個對象有get()、set()方法。解決的是變量在不同線程間的隔離性。也就是不同線程可以有自己的值,並放在ThreadLocal中保存。

(2)InheritableThreadLocal類,可以在子線程中取得父線程繼承下來的值。

 

4.Lock的使用

4.1 ReentrantLock類,目的是為了實現同步異步,比synchronized更加靈活。

(1)首先 Lock lock =new ReentrantLock(); 然后在需要同步的方法中的首行使用lock.lock(),方法的結束行使用lock.unlock()。效果和使用synchronized關鍵字一樣。當然也可以去鎖一部分代碼塊。

(2)Condition對象實現指定線程的等待、通知,相當於wait和notify升級版。Synchronized相當於只有一個condition對象。

首先 Lock lock =new ReentrantLock();

     Condition condition=new Condition();

在方法中先調用lock.lock()獲得同步監視器(否則報異常),然后用condition.await()來讓這個線程進入阻塞隊列。

(3)使用Condition實現等待通知

conditionA.await()進入阻塞,conditionA.signal()喚醒指定的這個線程。

signalAll()相當於notifyAll。

(4)使用多個Condition實現通知部分線程

  Condition conditionA=new Condition();

  Condition conditionB=new Condition();

conditionA.await()只有conditionA.signal(All)可以喚醒

(5)也可以實現生產者消費者模式,lock.lock()和signall方法。

(6)公平鎖:線程獲取的順序是按照線程加鎖的順序來分配的,先來先得。非公平鎖,采用搶占機制,隨機獲得鎖。reentranLock類默認情況使用的是非公平鎖。

(7)int getHoldCount()返回當前線程調用lock()方法的次數。

(8)int getQueueLength()返回正在等待獲取此鎖定的線程估計數。

(9)int getWaitQueueLength(Condition condition )返回等待此鎖定相關的給定條件condition的線程估計數。

(10)boolean hasQueuedThread(thread) 查詢指定線程是否在等待此鎖,

方法boolean hasWaiters(Condition condition) 作用是查詢是否有線程正在等待與此鎖定有關的condition條件。

(11)可使用多個Condition對象實現業務的順序執行。交替鏈接喚醒。

A方法中 conditionA.await()---conditionB.signalAll--

B方法中 conditionB.await()---conditionC.signalAll--

C方法中 conditionC.await()---conditionA.signalAll--

 

4.2ReentrantReadWriteLock類

讀鎖是共享鎖,寫鎖是排他鎖,讀讀共享是異步的,寫讀、寫寫都是互斥的同步的。

(1)ReentrantReadWriteLock類實現讀讀共享

ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

 lock.readLock().lock();

……代碼段

lock.readLock().unlock()

是異步的

(2)ReentrantReadWriteLock類實現寫寫互斥

ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

 lock.WriteLock().lock();

……代碼段

lock.WriteLock().unlock()

是同步的

(3)ReentrantReadWriteLock類實現讀寫互斥

ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

方法1

 lock.readLock().lock();

……代碼段

lock.readLock().unlock()

方法2

lock.WriteLock().lock();

……代碼段

lock.WriteLock().unlock()

兩個方法是互斥的。

 

5.單例模式與多線程

(1)立即加載/餓漢模式

立即加載是在使用類的時候對象就已經創建完畢,常見的實現辦法就是new實例化。

Public class MyObject{

private static MyObject myObject=new MyObject();

private MyObject(){

}

public static MyObject getInstance(){

Return myObject

}

}

(2)延遲加載/懶漢模式

在調用getInstance()方法時實例才被創建,在方法中實例化對象。

Public class MyObject{

private static MyObject myObject;

private MyObject(){

}

public static MyObject getInstance(){

if(myObject!=null){

 

}else{

myObject=new MyObject();

}

return myObject

}

}

懶漢模式在多線程環境下容易出錯,無法保持單例。

解決方案:

1.方法聲明synchronized-但是效率低下

2.同步代碼塊—(全部方法)效率低,(關鍵語句)無法單例。

3.同步代碼塊,使用DCL雙檢測,多數采用這種方式。即是說在內部關鍵方法里再判斷一次。

Public class MyObject{

Valotile private static MyObject myObject;

Public static MyObject getInstance(){

if(myObject!=null){

}else{

Thread.sleep(3000);

Synchronized(MyObject.class){

If(myObject==null){

myObject=new MyObject();

 

}

}

}

return myObject

}

}

4.static代碼塊實現單例模式

利用靜態代碼塊在使用類時就已經執行的特點。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM