摘要:轉(zhuǎn)載請(qǐng)備注地址多線程學(xué)習(xí)二將分為兩篇文章介紹同步方法另一篇介紹同步語(yǔ)句塊。如果兩個(gè)線程同時(shí)操作對(duì)象中的實(shí)例變量,則會(huì)出現(xiàn)非線程安全,解決辦法就是在方法前加上關(guān)鍵字即可。
轉(zhuǎn)載請(qǐng)備注地址: https://blog.csdn.net/qq_3433...
Java多線程學(xué)習(xí)(二)將分為兩篇文章介紹synchronized同步方法另一篇介紹synchronized同步語(yǔ)句塊。
系列文章傳送門(mén):
Java多線程學(xué)習(xí)(一)Java多線程入門(mén)
Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1)
java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(2)
Java多線程學(xué)習(xí)(三)volatile關(guān)鍵字
Java多線程學(xué)習(xí)(四)等待/通知(wait/notify)機(jī)制
Java多線程學(xué)習(xí)(五)線程間通信知識(shí)點(diǎn)補(bǔ)充
Java多線程學(xué)習(xí)(六)Lock鎖的使用
Java多線程學(xué)習(xí)(七)并發(fā)編程中一些問(wèn)題
系列文章將被優(yōu)先更新與微信公眾號(hào)“Java面試通關(guān)手冊(cè)”,歡迎廣大Java程序員和愛(ài)好技術(shù)的人員關(guān)注。
(1) synchronized同步方法本節(jié)思維導(dǎo)圖:
思維導(dǎo)圖源文件+思維導(dǎo)圖軟件關(guān)注微信公眾號(hào):“Java面試通關(guān)手冊(cè)”回復(fù)關(guān)鍵字:“Java多線程”免費(fèi)領(lǐng)取。
Java并發(fā)編程這個(gè)領(lǐng)域中synchronized關(guān)鍵字一直都是元老級(jí)的角色,很久之前很多人都會(huì)稱(chēng)它為“重量級(jí)鎖”。但是,在JavaSE 1.6之后進(jìn)行了主要包括為了減少獲得鎖和釋放鎖帶來(lái)的性能消耗而引入的偏向鎖和輕量級(jí)鎖以及其它各種優(yōu)化之后變得在某些情況下并不是那么重了。
這一篇文章不會(huì)介紹synchronized關(guān)鍵字的實(shí)現(xiàn)原理,更多的是synchronized關(guān)鍵字的使用。如果想要了解的可以看看方騰飛的《Java并發(fā)編程的藝術(shù)》。
二 變量安全性“非線程安全”問(wèn)題存在于“實(shí)例變量”中,如果是方法內(nèi)部的私有變量,則不存在“非線程安全”問(wèn)題,所得結(jié)果也就是“線程安全”的了。
如果兩個(gè)線程同時(shí)操作對(duì)象中的實(shí)例變量,則會(huì)出現(xiàn)“非線程安全”,解決辦法就是在方法前加上synchronized關(guān)鍵字即可。前面一篇文章我們已經(jīng)講過(guò),而且貼過(guò)相應(yīng)代碼,所以這里就不再貼代碼了。
三 多個(gè)對(duì)象多個(gè)鎖先看例子:
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),那么運(yùn)行結(jié)果就會(huì)顯示為同步的效果 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(); } }
運(yùn)行結(jié)果:
a num=100停頓一會(huì)才執(zhí)行
上面實(shí)例中兩個(gè)線程ThreadA和ThreadB分別訪問(wèn)同一個(gè)類(lèi)的不同實(shí)例的相同名稱(chēng)的同步方法,但是效果確實(shí)異步執(zhí)行。
為什么會(huì)這樣呢?
這是因?yàn)?font color="red">synchronized取得的鎖都是對(duì)象鎖,而不是把一段代碼或方法當(dāng)做鎖。所以在上面的實(shí)例中,哪個(gè)線程先執(zhí)行帶synchronized關(guān)鍵字的方法,則哪個(gè)線程就持有該方法所屬對(duì)象的鎖Lock,那么其他線程只能呈等待狀態(tài),前提是多個(gè)線程訪問(wèn)的是同一個(gè)對(duì)象。本例中很顯然是兩個(gè)對(duì)象。
在本例中創(chuàng)建了兩個(gè)HasSelfPrivateNum類(lèi)對(duì)象,所以就產(chǎn)生了兩個(gè)鎖。當(dāng)ThreadA的引用執(zhí)行到addI方法中的runThread.sleep(2000)語(yǔ)句時(shí),ThreadB就會(huì)“乘機(jī)執(zhí)行”。所以才會(huì)導(dǎo)致執(zhí)行結(jié)果如上圖所示(備注:由于runThread.sleep(2000),“a num=100”停頓了兩秒才輸出)
四 synchronized方法與鎖對(duì)象通過(guò)上面我們知道synchronized取得的鎖都是對(duì)象鎖,而不是把一段代碼或方法當(dāng)做鎖。如果多個(gè)線程訪問(wèn)的是同一個(gè)對(duì)象,哪個(gè)線程先執(zhí)行帶synchronized關(guān)鍵字的方法,則哪個(gè)線程就持有該方法,那么其他線程只能呈等待狀態(tài)。如果多個(gè)線程訪問(wèn)的是多個(gè)對(duì)象則不一定,因?yàn)槎鄠€(gè)對(duì)象會(huì)產(chǎn)生多個(gè)鎖。
那么我們思考一下當(dāng)多個(gè)線程訪問(wèn)的是同一個(gè)對(duì)象中的非synchronized類(lèi)型方法會(huì)是什么效果?
答案是:會(huì)異步調(diào)用非synchronized類(lèi)型方法,解決辦法也很簡(jiǎn)單在非synchronized類(lèi)型方法前加上synchronized關(guān)鍵字即可。
五 臟讀發(fā)生臟讀的情況實(shí)在讀取實(shí)例變量時(shí),此值已經(jīng)被其他線程更改過(guò)。
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關(guān)鍵字就同步了 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);//打印結(jié)果受此值大小影響 publicVarRef.getValue(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
運(yùn)行結(jié)果:
解決辦法:getValue()方法前加上synchronized關(guān)鍵字即可。
加上synchronized關(guān)鍵字后的運(yùn)行結(jié)果:
“可重入鎖”概念是:自己可以再次獲取自己的內(nèi)部鎖。比如一個(gè)線程獲得了某個(gè)對(duì)象的鎖,此時(shí)這個(gè)對(duì)象鎖還沒(méi)有釋放,當(dāng)其再次想要獲取這個(gè)對(duì)象的鎖的時(shí)候還是可以獲取的,如果不可鎖重入的話,就會(huì)造成死鎖。
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(); } }
運(yùn)行結(jié)果:
另外可重入鎖也支持在父子類(lèi)繼承的環(huán)境中
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(); } }
運(yùn)行結(jié)果:
說(shuō)明當(dāng)存在父子類(lèi)繼承關(guān)系時(shí),子類(lèi)是完全可以通過(guò)“可重入鎖”調(diào)用父類(lèi)的同步方法。
另外出現(xiàn)異常時(shí),其鎖持有的鎖會(huì)自動(dòng)釋放。
七 同步不具有繼承性如果父類(lèi)有一個(gè)帶synchronized關(guān)鍵字的方法,子類(lèi)繼承并重寫(xiě)了這個(gè)方法。
但是同步不能繼承,所以還是需要在子類(lèi)方法中添加synchronized關(guān)鍵字。
參考:
《Java多線程編程核心技術(shù)》
歡迎關(guān)注我的微信公眾號(hào):"Java面試通關(guān)手冊(cè)"(分享各種Java學(xué)習(xí)資源,面試題,以及企業(yè)級(jí)Java實(shí)戰(zhàn)項(xiàng)目回復(fù)關(guān)鍵字免費(fèi)領(lǐng)取):
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/69103.html
摘要:關(guān)鍵字加到非靜態(tài)方法上持有的是對(duì)象鎖。線程和線程持有的鎖不一樣,所以和運(yùn)行同步,但是和運(yùn)行不同步。所以盡量不要使用而使用參考多線程編程核心技術(shù)并發(fā)編程的藝術(shù)如果你覺(jué)得博主的文章不錯(cuò),歡迎轉(zhuǎn)發(fā)點(diǎn)贊。 系列文章傳送門(mén): Java多線程學(xué)習(xí)(一)Java多線程入門(mén) Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(2) J...
摘要:運(yùn)行可運(yùn)行狀態(tài)的線程獲得了時(shí)間片,執(zhí)行程序代碼。阻塞的情況分三種一等待阻塞運(yùn)行的線程執(zhí)行方法,會(huì)把該線程放入等待隊(duì)列中。死亡線程方法執(zhí)行結(jié)束,或者因異常退出了方法,則該線程結(jié)束生命周期。死亡的線程不可再次復(fù)生。 系列文章傳送門(mén): Java多線程學(xué)習(xí)(一)Java多線程入門(mén) Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線程學(xué)習(xí)(二)synchronized關(guān)鍵...
摘要:三關(guān)鍵字能保證原子性嗎并發(fā)編程藝術(shù)這本書(shū)上說(shuō)保證但是在自增操作非原子操作上不保證,多線程編程核心藝術(shù)這本書(shū)說(shuō)不保證。多線程訪問(wèn)關(guān)鍵字不會(huì)發(fā)生阻塞,而關(guān)鍵字可能會(huì)發(fā)生阻塞關(guān)鍵字能保證數(shù)據(jù)的可見(jiàn)性,但不能保證數(shù)據(jù)的原子性。 系列文章傳送門(mén): Java多線程學(xué)習(xí)(一)Java多線程入門(mén) Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線程學(xué)習(xí)(二)synchroniz...
摘要:今天開(kāi)始整理學(xué)習(xí)多線程的知識(shí),談?wù)勛钪匾膬蓚€(gè)關(guān)鍵字和。但是這樣一個(gè)過(guò)程比較慢,在使用多線程的時(shí)候就會(huì)出現(xiàn)問(wèn)題。有序性有序性是指多線程執(zhí)行結(jié)果的正確性。這種機(jī)制在多線程中會(huì)出現(xiàn)問(wèn)題,因此可以通過(guò)來(lái)禁止重排。 今天開(kāi)始整理學(xué)習(xí)多線程的知識(shí),談?wù)勛钪匾膬蓚€(gè)關(guān)鍵字:volatile和synchronized。 一、三個(gè)特性 1、原子性 所謂原子性操作就是指這些操作是不可中斷的,要么執(zhí)行過(guò)程...
摘要:無(wú)論是互斥鎖,還是自旋鎖,在任何時(shí)刻,最多只能有一個(gè)保持者,也就說(shuō),在任何時(shí)刻最多只能有一個(gè)執(zhí)行單元獲得鎖。另外在中引入了自適應(yīng)的自旋鎖。和關(guān)鍵字的總結(jié)推薦一 該文已加入開(kāi)源文檔:JavaGuide(一份涵蓋大部分Java程序員所需要掌握的核心知識(shí))。地址:https://github.com/Snailclimb... 本文是對(duì) synchronized 關(guān)鍵字使用、底層原理、JD...
閱讀 1273·2023-04-25 23:22
閱讀 1673·2023-04-25 20:04
閱讀 2648·2021-11-22 15:24
閱讀 2806·2021-11-11 16:54
閱讀 1887·2019-08-30 14:03
閱讀 1485·2019-08-29 16:35
閱讀 1706·2019-08-26 10:29
閱讀 2661·2019-08-23 18:01