Java多線程編程學習chapter2

java多線程編程核心技術chapter2


多線程的同步,也就是在Java語言中寫出線程安全的程序,,如何解決非線程相關的問題
1.synchronized對象監測器爲Object時的使用
2.synchronized對象監測器爲Class時的使用
3.非線程安全是如何出現的
4.關鍵字volatile的主要作用
5.關鍵字volatile與synchronized的區別和使用情況

線程不安全
在多個線程對同一個對象中的實例變量進行併發訪問時發生,出現髒讀,也就是取到的數據是被更改過的
線程安全
獲得的實例變量是經過同步處理的,不會出現髒讀

非線程安全的問題存在於實例變量中
方法內的變量爲線程安全的,這是方法內部的變量是私有的特性造成的
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\functionfieldsafe\FunctionFieldSafe.java

兩個線程訪問一個沒有同步的方法,同時操作業務對象中的實例變量,則有可能出現非線程安全的
解決辦法就是加上synchronized關鍵字
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\objectfieldsafe\ObjectFieldUnSafe.java
解決辦法就是加上synchronized關鍵字 synchronizedpublic void addI(String username)

結論:兩個線程訪問同一個對象中的同步方法時一定是線程安全的

多個對象多個鎖
當兩個線程分別訪問同一個類的兩個不同實例的相同名稱的同步方法,效果是異步執行的
關鍵字synchronized取得的鎖是對象鎖,並不是把一個代碼片段當作鎖
哪個線程先執行帶synchronized關鍵字的方法,哪個線程就持有該方法所屬的鎖Lock,那麼其他線程就只能等待,前提是多個線程訪問的是同一個對象。
如果是多個線程多個對象,則JVM會創建多個鎖。互不干擾。
synchronized, asynchronized
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\objectfieldsafe\MultiObjectLock.java

synchronized和鎖對象
調用關鍵字synchronized聲明的方法一定是排隊運行的,只有共享資源的讀寫訪問才需要同步化,如果不是共享資源,根本沒有同步的必要
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\synmethodlockobject\SynMethodLockObject.java
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\synmethodlockobject\SynMethodLockObject1.java

結論:A線程先持有objec對象的Lock鎖,B線程可以以異步的方式調用objec對象中的非synchronized類型的方法
A線程先持有object對象的Lock鎖,B線程如果這時調用object對象中的synchronized類型的方法則需等待,也就是同步
參照代碼:\SimpleThread\src\com\nineclient\call\chapter2\synmethodlockobject\TwoMethodLockOne.java
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\synmethodlockobject\TwoMethodLockTwo.java

髒讀
發生髒讀的情況是在讀取實例變量時,此值已經被其他線程更改過
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\publicvar\PublicVar.java
加上synchronized,就可以解決髒讀

synchronized鎖重入
可鎖重入,自己可以再次獲取自己的內部鎖,比如一條線程獲得了某個對象的鎖,此時這個對象鎖還沒有釋放,當其再次想要獲取這個對象鎖的時候還是可以獲取的,如果不可鎖重入會造成死鎖
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\publicvar\Service1_3.java

可鎖重入也支持在父子類繼承環境中,子類完全可以通過可鎖重入調用父類的同步方法
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\publicvar\SubFather.java

當一個線程執行的代碼出現異常時,其所持有的鎖自動釋放
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\releaselock\ReleaseLockWhenException.java

同步不具有繼承性
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\releaselock\SynchronizedNOExtend.java

synchronized同步語句塊
synchronized方法是對當前對象進行加鎖,synchronized代碼塊是對某一個對象進行加鎖
synchronized弊端,假如處理一個長時間的任務,其他線程等待時間長
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\baddot\BadDot.java

synchronized同步代碼塊的使用
當兩個併發線程訪問同一個對象Object中的synchronized(this)同步代碼塊時,一段時間內只能有一個線程被執行,另一個線程必須等待當前線程執行完這個代碼塊以後才能執行該代碼塊
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\baddot\SynchronizedBlock.java
執行效果還是同步的

用同步代碼塊來解決運行時間長的弊端
當一個線程訪問object的一個synchronized同步代碼塊時,另一個線程仍然可以訪問該object對象中的非synchronized(this)代碼塊
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\quick\Quickly.java
結論:synchronized同步代碼塊的確是同步的,真的持有當前對象的鎖

一半異步,一般同步
不在synchronized塊中的就是異步,在synchronized塊中的就是同步執行
參照代碼:

synchronized代碼塊之間的同步性
當一個線程訪問一個對象的synchronized(this)代碼塊時,其他線程對同一個object中所有其他synchronized(this)同步代碼塊將被阻塞,這說明synhcronized使用的是同一個對象監視器
參照代碼:

驗證同步synchronized(this)代碼塊鎖定的是當前對象和synchronzied方法一樣
參照代碼:

將任意對象作爲對象監視器,同一時間只有一個synchronized(非this)同步代碼塊可以執行
這個任意對象大多數是實例變量及方法的參數,使用格式爲synchronized(非this對象)
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\anyobject\AnyObject.java

synchronized(非this對象)和synchronized方法是一步的,不和其他鎖this同步方法爭搶this鎖
如果不是同一個對象監視器,運行結果是異步的
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\diffobject\DifferentObject.java
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\diffobject\DiffObject1.java

線程調用方法的順序是無序的
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\diffobject\ThreadNoSort.java
由於兩個線程方法的順序不確定,所以當兩個線程執行帶有分支判斷的方法時,就會出現邏輯上的錯誤,可能出現髒讀
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\anyobject\MyOneObject.java

驗證是三個結論
當多個線程同時執行synchronized(x) 同步代碼塊時呈同步效果
當其他線程執行x對象中的synchronized同步方法時呈同步效果
當其他線程執行x對象中的synchronized(this)代碼塊時也呈同步效果
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\checkthree\CheckThreeResult.java
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\checkthree\CheckThreeResult1.java
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\checkthree\CheckThreeResult2.java

靜態同步synchronized方法和synchronized(class)代碼塊
關鍵字synchronized還可以應用在static靜態方法上,那是對當前.java文件對應的class文件進行加鎖
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\synchronizedclass\SynchronizedClass.java

驗證synchronzied加在非static方法上是對象鎖,synchronzied加到static方法上是給class類上鎖    
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\synchronizedclass\DiffObjectLock.java

一個對象鎖,一個class鎖,Class鎖對所有的對象實例都起作用
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\synchronizedclass\MoreObjectOneSynObject.java

synchronized(class) 代碼塊的作用和synchronized static 方法作用是一樣的
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\synchronizedclass\SynObjectSynBlock.java

數據類型的String的常量池特性
JVM中具有String常量池緩存功能。
當傳入的String字符串參數相同時,兩個線程就具有相同的鎖
解決辦法是同步代碼塊都不使用String作爲鎖對象
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\stringobject\StringObject.java

同步synchronzied方法無線等待與解決,用同步代碼塊解決
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\stringobject\DeadLock.java

多線程死鎖
因爲不同的線程都在等待根本不可能被釋放的鎖,從而導致所有的任務都無法繼續完成,死鎖是必須避免的,以爲是說會造成線程假死
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\stringobject\DeathThread.java
到JDK安裝目錄的bin目錄下,執行jps,得到線程run的id值是假如是000,再執行jstack命令,jstack -l 000

內置類和靜態內置類
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\Innerclass\InnerClass.java
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\Innerclass\InnerClass1.java
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\Innerclass\PublicClass.java
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\Innerclass\PublicClass1.java

內置類中有兩個同步方法,不同的鎖,是異步執行的
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\Innerclass\InnerMain.java

鎖對象的改變
只要是鎖的對象獲得的時候是不一樣的,執行就是異步的
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\changeobject\ChangeLockObject.java

只要對象不變,即使對象的屬性被改變,運行結果還是同步的
參照代碼:

volatile關鍵字
作用是使變量在多個線程之間可見
關鍵字volatile與死循環
同步死循環的例子
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\changeobject\DealSynDeadLock.java
上面的例子放在server上,還是會出現死循環,解決辦法是用volatile關鍵字
privateboolean isCon = true;
存在於公共堆棧和線程私有堆棧中,在JVM設置成-server模式時,爲了線程的執行效率,線程一直是在私有堆棧中取得iscon的值,而代碼a.setCon(false);雖然被執行,但是更新的是公共堆棧的變量,所以一直是死循環
volatile和synchronized的比較
volatile是線程同步的輕量級實現,性能比synchronized好,volatile只能修飾變量,synchronized可以修飾方法和代碼塊
多線程訪問volatile變量時,不會阻塞,而synchronized會阻塞
volatile保證數據的可見性,但是不能保證原子性,synchronzed保證了原子性,也間接保證了可見性
volatile解決的是變量在多個線程之間的可見性,而synchronized關鍵字解決的是多個線程之間同步性

解決同步死循環,多線程解決
解決異步死循環,用關鍵字volatile關鍵字,強制從公共堆棧中進行取值,感知實例變量被更改了,多線程讀取共享變量時可以獲得最新的值。
volatile保證可見性,不保證原子性
i++是線程不安全的,從內存中讀取i值,計算i的值,將i值寫到內存中
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\changeobject\VolatileNoSafe.java

read  load
use assign
store write
原子類型,在沒有鎖的情況下,做到線程安全
原子類也不一定安全:方法是原子的,但是方法與方法之間的調用卻不是原子的,解決這樣的問題是要同步
原子類有:AtomicInteger AtomicLong

最後一個列子好欣慰
還是工作內存和主內存的數據不一致
參照代碼:SimpleThread\src\com\nineclient\call\chapter2\changeobject\GoodService.java
            while(iscon) {
                  synchronized("aaa") {
                  }
            }

保證可視性和同步性

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章