摘要:轉載請備注地址多線程學習二將分為兩篇文章介紹同步方法另一篇介紹同步語句塊。如果兩個線程同時操作對象中的實例變量,則會出現非線程安全,解決辦法就是在方法前加上關鍵字即可。
轉載請備注地址: https://blog.csdn.net/qq_3433...
Java多線程學習(二)將分為兩篇文章介紹synchronized同步方法另一篇介紹synchronized同步語句塊。
系列文章傳送門:
Java多線程學習(一)Java多線程入門
Java多線程學習(二)synchronized關鍵字(1)
java多線程學習(二)synchronized關鍵字(2)
Java多線程學習(三)volatile關鍵字
Java多線程學習(四)等待/通知(wait/notify)機制
Java多線程學習(五)線程間通信知識點補充
Java多線程學習(六)Lock鎖的使用
Java多線程學習(七)并發編程中一些問題
系列文章將被優先更新與微信公眾號“Java面試通關手冊”,歡迎廣大Java程序員和愛好技術的人員關注。
(1) synchronized同步方法本節思維導圖:
思維導圖源文件+思維導圖軟件關注微信公眾號:“Java面試通關手冊”回復關鍵字:“Java多線程”免費領取。
Java并發編程這個領域中synchronized關鍵字一直都是元老級的角色,很久之前很多人都會稱它為“重量級鎖”。但是,在JavaSE 1.6之后進行了主要包括為了減少獲得鎖和釋放鎖帶來的性能消耗而引入的偏向鎖和輕量級鎖以及其它各種優化之后變得在某些情況下并不是那么重了。
這一篇文章不會介紹synchronized關鍵字的實現原理,更多的是synchronized關鍵字的使用。如果想要了解的可以看看方騰飛的《Java并發編程的藝術》。
二 變量安全性“非線程安全”問題存在于“實例變量”中,如果是方法內部的私有變量,則不存在“非線程安全”問題,所得結果也就是“線程安全”的了。
如果兩個線程同時操作對象中的實例變量,則會出現“非線程安全”,解決辦法就是在方法前加上synchronized關鍵字即可。前面一篇文章我們已經講過,而且貼過相應代碼,所以這里就不再貼代碼了。
三 多個對象多個鎖先看例子:
HasSelfPrivateNum.java
public class HasSelfPrivateNum { private int num = 0; synchronized public void addI(String username) { try { if (username.equals("a")) { num = 100; System.out.println("a set over!"); //如果去掉hread.sleep(2000),那么運行結果就會顯示為同步的效果 Thread.sleep(2000); } else { num = 200; System.out.println("b set over!"); } System.out.println(username + " num=" + num); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
ThreadA.java
public class ThreadA extends Thread { private HasSelfPrivateNum numRef; public ThreadA(HasSelfPrivateNum numRef) { super(); this.numRef = numRef; } @Override public void run() { super.run(); numRef.addI("a"); } }
ThreadB.java
public class ThreadB extends Thread { private HasSelfPrivateNum numRef; public ThreadB(HasSelfPrivateNum numRef) { super(); this.numRef = numRef; } @Override public void run() { super.run(); numRef.addI("b"); } }
Run.java
public class Run { public static void main(String[] args) { HasSelfPrivateNum numRef1 = new HasSelfPrivateNum(); HasSelfPrivateNum numRef2 = new HasSelfPrivateNum(); ThreadA athread = new ThreadA(numRef1); athread.start(); ThreadB bthread = new ThreadB(numRef2); bthread.start(); } }
運行結果:
a num=100停頓一會才執行
上面實例中兩個線程ThreadA和ThreadB分別訪問同一個類的不同實例的相同名稱的同步方法,但是效果確實異步執行。
為什么會這樣呢?
這是因為synchronized取得的鎖都是對象鎖,而不是把一段代碼或方法當做鎖。所以在上面的實例中,哪個線程先執行帶synchronized關鍵字的方法,則哪個線程就持有該方法所屬對象的鎖Lock,那么其他線程只能呈等待狀態,前提是多個線程訪問的是同一個對象。本例中很顯然是兩個對象。
在本例中創建了兩個HasSelfPrivateNum類對象,所以就產生了兩個鎖。當ThreadA的引用執行到addI方法中的runThread.sleep(2000)語句時,ThreadB就會“乘機執行”。所以才會導致執行結果如上圖所示(備注:由于runThread.sleep(2000),“a num=100”停頓了兩秒才輸出)
四 synchronized方法與鎖對象通過上面我們知道synchronized取得的鎖都是對象鎖,而不是把一段代碼或方法當做鎖。如果多個線程訪問的是同一個對象,哪個線程先執行帶synchronized關鍵字的方法,則哪個線程就持有該方法,那么其他線程只能呈等待狀態。如果多個線程訪問的是多個對象則不一定,因為多個對象會產生多個鎖。
那么我們思考一下當多個線程訪問的是同一個對象中的非synchronized類型方法會是什么效果?
答案是:會異步調用非synchronized類型方法,解決辦法也很簡單在非synchronized類型方法前加上synchronized關鍵字即可。
五 臟讀發生臟讀的情況實在讀取實例變量時,此值已經被其他線程更改過。
PublicVar.java
public class PublicVar { public String username = "A"; public String password = "AA"; synchronized public void setValue(String username, String password) { try { this.username = username; Thread.sleep(5000); this.password = password; System.out.println("setValue method thread name=" + Thread.currentThread().getName() + " username=" + username + " password=" + password); } catch (InterruptedException e) { e.printStackTrace(); } } //該方法前加上synchronized關鍵字就同步了 public void getValue() { System.out.println("getValue method thread name=" + Thread.currentThread().getName() + " username=" + username + " password=" + password); } }
ThreadA.java
public class ThreadA extends Thread { private PublicVar publicVar; public ThreadA(PublicVar publicVar) { super(); this.publicVar = publicVar; } @Override public void run() { super.run(); publicVar.setValue("B", "BB"); } }
Test.java
public class Test { public static void main(String[] args) { try { PublicVar publicVarRef = new PublicVar(); ThreadA thread = new ThreadA(publicVarRef); thread.start(); Thread.sleep(200);//打印結果受此值大小影響 publicVarRef.getValue(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
運行結果:
解決辦法:getValue()方法前加上synchronized關鍵字即可。
加上synchronized關鍵字后的運行結果:
“可重入鎖”概念是:自己可以再次獲取自己的內部鎖。比如一個線程獲得了某個對象的鎖,此時這個對象鎖還沒有釋放,當其再次想要獲取這個對象的鎖的時候還是可以獲取的,如果不可鎖重入的話,就會造成死鎖。
Service.java
public class Service { synchronized public void service1() { System.out.println("service1"); service2(); } synchronized public void service2() { System.out.println("service2"); service3(); } synchronized public void service3() { System.out.println("service3"); } }
MyThread.java
public class MyThread extends Thread { @Override public void run() { Service service = new Service(); service.service1(); } }
Run.java
public class Run { public static void main(String[] args) { MyThread t = new MyThread(); t.start(); } }
運行結果:
另外可重入鎖也支持在父子類繼承的環境中
Main.java:
public class Main { public int i = 10; synchronized public void operateIMainMethod() { try { i--; System.out.println("main print i=" + i); Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Sub.java:
public class Sub extends Main { synchronized public void operateISubMethod() { try { while (i > 0) { i--; System.out.println("sub print i=" + i); Thread.sleep(100); this.operateIMainMethod(); } } catch (InterruptedException e) { e.printStackTrace(); } } }
MyThread.java:
public class MyThread extends Thread { @Override public void run() { Sub sub = new Sub(); sub.operateISubMethod(); } }
Run.java:
public class Run { public static void main(String[] args) { MyThread t = new MyThread(); t.start(); } }
運行結果:
說明當存在父子類繼承關系時,子類是完全可以通過“可重入鎖”調用父類的同步方法。
另外出現異常時,其鎖持有的鎖會自動釋放。
七 同步不具有繼承性如果父類有一個帶synchronized關鍵字的方法,子類繼承并重寫了這個方法。
但是同步不能繼承,所以還是需要在子類方法中添加synchronized關鍵字。
參考:
《Java多線程編程核心技術》
歡迎關注我的微信公眾號:"Java面試通關手冊"(分享各種Java學習資源,面試題,以及企業級Java實戰項目回復關鍵字免費領取):
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/69103.html
摘要:關鍵字加到非靜態方法上持有的是對象鎖。線程和線程持有的鎖不一樣,所以和運行同步,但是和運行不同步。所以盡量不要使用而使用參考多線程編程核心技術并發編程的藝術如果你覺得博主的文章不錯,歡迎轉發點贊。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Java多線程學習(二)synchronized關鍵字(1) java多線程學習(二)synchronized關鍵字(2) J...
摘要:運行可運行狀態的線程獲得了時間片,執行程序代碼。阻塞的情況分三種一等待阻塞運行的線程執行方法,會把該線程放入等待隊列中。死亡線程方法執行結束,或者因異常退出了方法,則該線程結束生命周期。死亡的線程不可再次復生。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Java多線程學習(二)synchronized關鍵字(1) java多線程學習(二)synchronized關鍵...
摘要:三關鍵字能保證原子性嗎并發編程藝術這本書上說保證但是在自增操作非原子操作上不保證,多線程編程核心藝術這本書說不保證。多線程訪問關鍵字不會發生阻塞,而關鍵字可能會發生阻塞關鍵字能保證數據的可見性,但不能保證數據的原子性。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Java多線程學習(二)synchronized關鍵字(1) java多線程學習(二)synchroniz...
摘要:今天開始整理學習多線程的知識,談談最重要的兩個關鍵字和。但是這樣一個過程比較慢,在使用多線程的時候就會出現問題。有序性有序性是指多線程執行結果的正確性。這種機制在多線程中會出現問題,因此可以通過來禁止重排。 今天開始整理學習多線程的知識,談談最重要的兩個關鍵字:volatile和synchronized。 一、三個特性 1、原子性 所謂原子性操作就是指這些操作是不可中斷的,要么執行過程...
摘要:無論是互斥鎖,還是自旋鎖,在任何時刻,最多只能有一個保持者,也就說,在任何時刻最多只能有一個執行單元獲得鎖。另外在中引入了自適應的自旋鎖。和關鍵字的總結推薦一 該文已加入開源文檔:JavaGuide(一份涵蓋大部分Java程序員所需要掌握的核心知識)。地址:https://github.com/Snailclimb... 本文是對 synchronized 關鍵字使用、底層原理、JD...
閱讀 1275·2023-04-25 23:22
閱讀 1675·2023-04-25 20:04
閱讀 2650·2021-11-22 15:24
閱讀 2811·2021-11-11 16:54
閱讀 1891·2019-08-30 14:03
閱讀 1490·2019-08-29 16:35
閱讀 1708·2019-08-26 10:29
閱讀 2670·2019-08-23 18:01