本文承接一併發編程(JAVA版)-------------(一)
本文承接二併發編程(JAVA版)-------------(二)
共享模型之管程
面對共享資源的重要知識:原子性,對象的選擇
- synchronized實際是用對象鎖保證了臨界區內代碼的原子性,臨界區內的代碼對外是不可分割的,不會被線程切換所打斷。
關於線程的理解,可以上網搜一下線程八鎖問題,這個是我在學習的過程中覺得非常不錯的關於線程理解的問題,看一下總歸是有好處的,你們隨意,要是覺得好的話,記得回來點個贊,支持一下博主,你們的支持是我最大的動力。
變量的線程安全分析
成員變量和靜態變量是否線程安全?
- 如果它們沒有共享,則線程安全
- 如果它們被共享了,根據它們的狀態是否能夠改變又分兩種狀態:
- 如果只有讀操作,則線程安全
- 如果有讀寫操作,則這段代碼是臨界區,需要考慮線程安全
局部變量是否線程安全?
- 局部變量是線程安全的
- 但局部變量引用的對象則未必
- 如果該對象沒有逃離方法的作用訪問,它是線程安全的
- 如果該對象逃離方法的作用範圍,則需要考慮線程安全(可用final、private來加強安全性)
常見的線程安全類
- String
- Integer
- StringBuffer
- Random
- Vector
- Hashtable
- jave.util.concurrent包下的類(簡稱JUC)
這裏說它們是線程安全的是指,多個線程調用它們同一個實例的某個方法時,線程是安全的。也可以理解爲:
Hashtable table = new Hashtable();
new Thread(()->{
table.put("key","value1");
}).start();
new Thread(()->{
table.put("key","value2");
}).start();
- 它們的每一個方法是原子的
- 但注意它們多個方法的組合不是原子的,見後面的分析
不可變類線程安全性
String、Integer等都是不可變類,因爲其內部的狀態不可以改變,因此它們的方法都是線程安全的。
說到這裏,肯定就有同學有疑問,String有replace、substring等方法【可以】改變值啊,那麼這些方法又是如何保證線程安全的呢?
答案是通過閱讀源碼可以瞭解,在這些方法裏通過新建對象來保證線程安全。
Monitor(鎖)工作原理
Monitor被翻譯爲監視器或管程
每個Java對象都可以關聯一個Monitor對象,如果使用synchronized給對象上鎖(重量級)之後,該對象頭的Mark Word中就被設置指向Monitor對象的指針
- 剛開始Monitor中Owner爲null
- 當Thread2執行synchronized(obj)就會將Monitor的所有者Owner置爲Thread2,Monitor中只能有一個Owner
- 在Thread2上鎖的過程中,如果Thread3,Thread4,Thread5也來執行synchronized(obj),就會進入EntryList BLOCKED
- Thread2執行完同步代碼塊的內容,然後喚醒EntryList中等待的線程來競爭鎖,競爭的時候是非公平的,是由JDK底層實現決定的
注意
- synchronized必須是進入同一個對象的monitor纔有上述效果
- 不加synchronized的對象不會關聯監視器,不遵循以上規則