摘要:關(guān)鍵字加到非靜態(tài)方法上持有的是對象鎖。線程和線程持有的鎖不一樣,所以和運行同步,但是和運行不同步。所以盡量不要使用而使用參考多線程編程核心技術(shù)并發(fā)編程的藝術(shù)如果你覺得博主的文章不錯,歡迎轉(zhuǎn)發(fā)點贊。
系列文章傳送門:
Java多線程學(xué)習(xí)(一)Java多線程入門
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)機制
Java多線程學(xué)習(xí)(五)線程間通信知識點補充
Java多線程學(xué)習(xí)(六)Lock鎖的使用
Java多線程學(xué)習(xí)(七)并發(fā)編程中一些問題
系列文章將被優(yōu)先更新于微信公眾號“Java面試通關(guān)手冊”,歡迎廣大Java程序員和愛好技術(shù)的人員關(guān)注。
(2) synchronized同步語句塊本節(jié)思維導(dǎo)圖:
思維導(dǎo)圖源文件+思維導(dǎo)圖軟件關(guān)注微信公眾號:“Java面試通關(guān)手冊”回復(fù)關(guān)鍵字:“Java多線程”免費領(lǐng)取。
一 synchronized方法的缺點使用synchronized關(guān)鍵字聲明方法有些時候是有很大的弊端的,比如我們有兩個線程一個線程A調(diào)用同步方法后獲得鎖,那么另一個線程B就需要等待A執(zhí)行完,但是如果說A執(zhí)行的是一個很費時間的任務(wù)的話這樣就會很耗時。
先來看一個暴露synchronized方法的缺點實例,然后在看看如何通過synchronized同步語句塊解決這個問題。
Task.java
public class Task { private String getData1; private String getData2; public synchronized void doLongTimeTask() { try { System.out.println("begin task"); Thread.sleep(3000); getData1 = "長時間處理任務(wù)后從遠程返回的值1 threadName=" + Thread.currentThread().getName(); getData2 = "長時間處理任務(wù)后從遠程返回的值2 threadName=" + Thread.currentThread().getName(); System.out.println(getData1); System.out.println(getData2); System.out.println("end task"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
CommonUtils.java
public class CommonUtils { public static long beginTime1; public static long endTime1; public static long beginTime2; public static long endTime2; }
MyThread1.java
public class MyThread1 extends Thread { private Task task; public MyThread1(Task task) { super(); this.task = task; } @Override public void run() { super.run(); CommonUtils.beginTime1 = System.currentTimeMillis(); task.doLongTimeTask(); CommonUtils.endTime1 = System.currentTimeMillis(); } }
MyThread2.java
public class MyThread2 extends Thread { private Task task; public MyThread2(Task task) { super(); this.task = task; } @Override public void run() { super.run(); CommonUtils.beginTime2 = System.currentTimeMillis(); task.doLongTimeTask(); CommonUtils.endTime2 = System.currentTimeMillis(); } }
Run.java
public class Run { public static void main(String[] args) { Task task = new Task(); MyThread1 thread1 = new MyThread1(task); thread1.start(); MyThread2 thread2 = new MyThread2(task); thread2.start(); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } long beginTime = CommonUtils.beginTime1; if (CommonUtils.beginTime2 < CommonUtils.beginTime1) { beginTime = CommonUtils.beginTime2; } long endTime = CommonUtils.endTime1; if (CommonUtils.endTime2 > CommonUtils.endTime1) { endTime = CommonUtils.endTime2; } System.out.println("耗時:" + ((endTime - beginTime) / 1000)); } }
運行結(jié)果:
從運行時間上來看,synchronized方法的問題很明顯??梢?font color="red">使用synchronized同步塊來解決這個問題。但是要注意synchronized同步塊的使用方式,如果synchronized同步塊使用不好的話并不會帶來效率的提升。
修改上例中的Task.java如下:
public class Task { private String getData1; private String getData2; public void doLongTimeTask() { try { System.out.println("begin task"); Thread.sleep(3000); String privateGetData1 = "長時間處理任務(wù)后從遠程返回的值1 threadName=" + Thread.currentThread().getName(); String privateGetData2 = "長時間處理任務(wù)后從遠程返回的值2 threadName=" + Thread.currentThread().getName(); synchronized (this) { getData1 = privateGetData1; getData2 = privateGetData2; } System.out.println(getData1); System.out.println(getData2); System.out.println("end task"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
運行結(jié)果:
從上面代碼可以看出當一個線程訪問一個對象的synchronized同步代碼塊時,另一個線程任然可以訪問該對象非synchronized同步代碼塊。
時間雖然縮短了,但是大家考慮一下synchronized代碼塊真的是同步的嗎?它真的持有當前調(diào)用對象的鎖嗎?
是的。不在synchronized代碼塊中就異步執(zhí)行,在synchronized代碼塊中就是同步執(zhí)行。
驗證代碼:synchronizedDemo1包下
三 synchronized(object)代碼塊間使用MyObject.java
public class MyObject { }
Service.java
public class Service { public void testMethod1(MyObject object) { synchronized (object) { try { System.out.println("testMethod1 ____getLock time=" + System.currentTimeMillis() + " run ThreadName=" + Thread.currentThread().getName()); Thread.sleep(2000); System.out.println("testMethod1 releaseLock time=" + System.currentTimeMillis() + " run ThreadName=" + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
ThreadA.java
public class ThreadA extends Thread { private Service service; private MyObject object; public ThreadA(Service service, MyObject object) { super(); this.service = service; this.object = object; } @Override public void run() { super.run(); service.testMethod1(object); } }
ThreadB.java
public class ThreadB extends Thread { private Service service; private MyObject object; public ThreadB(Service service, MyObject object) { super(); this.service = service; this.object = object; } @Override public void run() { super.run(); service.testMethod1(object); } }
Run1_1.java
public class Run1_1 { public static void main(String[] args) { Service service = new Service(); MyObject object = new MyObject(); ThreadA a = new ThreadA(service, object); a.setName("a"); a.start(); ThreadB b = new ThreadB(service, object); b.setName("b"); b.start(); } }
運行結(jié)果:
可以看出如下圖所示,兩個線程使用了同一個“對象監(jiān)視器”,所以運行結(jié)果是同步的。
那么,如果使用不同的對象監(jiān)視器會出現(xiàn)什么效果呢?
修改Run1_1.java如下:
public class Run1_2 { public static void main(String[] args) { Service service = new Service(); MyObject object1 = new MyObject(); MyObject object2 = new MyObject(); ThreadA a = new ThreadA(service, object1); a.setName("a"); a.start(); ThreadB b = new ThreadB(service, object2); b.setName("b"); b.start(); } }
運行結(jié)果:
可以看出如下圖所示,兩個線程使用了不同的“對象監(jiān)視器”,所以運行結(jié)果不是同步的了。
當一個對象訪問synchronized(this)代碼塊時,其他線程對同一個對象中所有其他synchronized(this)代碼塊代碼塊的訪問將被阻塞,這說明synchronized(this)代碼塊使用的“對象監(jiān)視器”是一個。
也就是說和synchronized方法一樣,synchronized(this)代碼塊也是鎖定當前對象的。
另外通過上面的學(xué)習(xí)我們可以得出兩個結(jié)論。
其他線程執(zhí)行對象中synchronized同步方法(上一節(jié)我們介紹過,需要回顧的可以看上一節(jié)的文章)和synchronized(this)代碼塊時呈現(xiàn)同步效果;
如果兩個線程使用了同一個“對象監(jiān)視器”,運行結(jié)果同步,否則不同步.
五 靜態(tài)同步synchronized方法與synchronized(class)代碼塊synchronized關(guān)鍵字加到static靜態(tài)方法和synchronized(class)代碼塊上都是是給Class類上鎖,而synchronized關(guān)鍵字加到非static靜態(tài)方法上是給對象上鎖。
Service.java
package ceshi; public class Service { public static void printA() { synchronized (Service.class) { try { System.out.println( "線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進入printA"); Thread.sleep(3000); System.out.println( "線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開printA"); } catch (InterruptedException e) { e.printStackTrace(); } } } synchronized public static void printB() { System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進入printB"); System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開printB"); } synchronized public void printC() { System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進入printC"); System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開printC"); } }
ThreadA.java
public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { super(); this.service = service; } @Override public void run() { service.printA(); } }
ThreadB.java
public class ThreadB extends Thread { private Service service; public ThreadB(Service service) { super(); this.service = service; } @Override public void run() { service.printB(); } }
ThreadC.java
public class ThreadC extends Thread { private Service service; public ThreadC(Service service) { super(); this.service = service; } @Override public void run() { service.printC(); } }
Run.java
public class Run { public static void main(String[] args) { Service service = new Service(); ThreadA a = new ThreadA(service); a.setName("A"); a.start(); ThreadB b = new ThreadB(service); b.setName("B"); b.start(); ThreadC c = new ThreadC(service); c.setName("C"); c.start(); } }
運行結(jié)果:
從運行結(jié)果可以看出:靜態(tài)同步synchronized方法與synchronized(class)代碼塊持有的鎖一樣,都是Class鎖,Class鎖對對象的所有實例起作用。synchronized關(guān)鍵字加到非static靜態(tài)方法上持有的是對象鎖。
線程A,B和線程C持有的鎖不一樣,所以A和B運行同步,但是和C運行不同步。
在Jvm中具有String常量池緩存的功能
String s1 = "a"; String s2="a"; System.out.println(s1==s2);//true
上面代碼輸出為true.這是為什么呢?
字符串常量池中的字符串只存在一份! 即執(zhí)行完第一行代碼后,常量池中已存在 “a”,那么s2不會在常量池中申請新的空間,而是直接把已存在的字符串內(nèi)存地址返回給s2。
因為數(shù)據(jù)類型String的常量池屬性,所以synchronized(string)在使用時某些情況下會出現(xiàn)一些問題,比如兩個線程運行
synchronized("abc"){
}和
synchronized("abc"){
}修飾的方法時,這兩個線程就會持有相同的鎖,導(dǎo)致某一時刻只有一個線程能運行。所以盡量不要使用synchronized(string)而使用synchronized(object)
參考:
《Java多線程編程核心技術(shù)》
《Java并發(fā)編程的藝術(shù)》
如果你覺得博主的文章不錯,歡迎轉(zhuǎn)發(fā)點贊。你能從中學(xué)到知識就是我最大的幸運。
歡迎關(guān)注我的微信公眾號:“Java面試通關(guān)手冊”(分享各種Java學(xué)習(xí)資源,面試題,以及企業(yè)級Java實戰(zhàn)項目回復(fù)關(guān)鍵字免費領(lǐng)?。?。另外我創(chuàng)建了一個Java學(xué)習(xí)交流群(群號:174594747),歡迎大家加入一起學(xué)習(xí),這里更有面試,學(xué)習(xí)視頻等資源的分享。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69102.html
摘要:轉(zhuǎn)載請備注地址多線程學(xué)習(xí)二將分為兩篇文章介紹同步方法另一篇介紹同步語句塊。如果兩個線程同時操作對象中的實例變量,則會出現(xiàn)非線程安全,解決辦法就是在方法前加上關(guān)鍵字即可。 轉(zhuǎn)載請備注地址: https://blog.csdn.net/qq_3433... Java多線程學(xué)習(xí)(二)將分為兩篇文章介紹synchronized同步方法另一篇介紹synchronized同步語句塊。系列文章傳送門...
摘要:運行可運行狀態(tài)的線程獲得了時間片,執(zhí)行程序代碼。阻塞的情況分三種一等待阻塞運行的線程執(zhí)行方法,會把該線程放入等待隊列中。死亡線程方法執(zhí)行結(jié)束,或者因異常退出了方法,則該線程結(jié)束生命周期。死亡的線程不可再次復(fù)生。 系列文章傳送門: Java多線程學(xué)習(xí)(一)Java多線程入門 Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線程學(xué)習(xí)(二)synchronized關(guān)鍵...
摘要:三關(guān)鍵字能保證原子性嗎并發(fā)編程藝術(shù)這本書上說保證但是在自增操作非原子操作上不保證,多線程編程核心藝術(shù)這本書說不保證。多線程訪問關(guān)鍵字不會發(fā)生阻塞,而關(guān)鍵字可能會發(fā)生阻塞關(guān)鍵字能保證數(shù)據(jù)的可見性,但不能保證數(shù)據(jù)的原子性。 系列文章傳送門: Java多線程學(xué)習(xí)(一)Java多線程入門 Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線程學(xué)習(xí)(二)synchroniz...
摘要:今天開始整理學(xué)習(xí)多線程的知識,談?wù)勛钪匾膬蓚€關(guān)鍵字和。但是這樣一個過程比較慢,在使用多線程的時候就會出現(xiàn)問題。有序性有序性是指多線程執(zhí)行結(jié)果的正確性。這種機制在多線程中會出現(xiàn)問題,因此可以通過來禁止重排。 今天開始整理學(xué)習(xí)多線程的知識,談?wù)勛钪匾膬蓚€關(guān)鍵字:volatile和synchronized。 一、三個特性 1、原子性 所謂原子性操作就是指這些操作是不可中斷的,要么執(zhí)行過程...
摘要:返回與此鎖相關(guān)聯(lián)的給定條件等待的線程數(shù)的估計。查詢是否有線程正在等待獲取此鎖。為公平鎖,為非公平鎖線程運行了獲得鎖定運行結(jié)果公平鎖的運行結(jié)果是有序的。 系列文章傳送門: Java多線程學(xué)習(xí)(一)Java多線程入門 Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(2) Java多線程學(xué)習(xí)(三)volatile關(guān)鍵字 ...
閱讀 552·2019-08-30 15:55
閱讀 960·2019-08-29 15:35
閱讀 1216·2019-08-29 13:48
閱讀 1926·2019-08-26 13:29
閱讀 2951·2019-08-23 18:26
閱讀 1264·2019-08-23 18:20
閱讀 2847·2019-08-23 16:43
閱讀 2721·2019-08-23 15:58