摘要:非靜態(tài)方法以及方法內(nèi)部的代碼塊持有的是同一個(gè)對(duì)象鎖,它們是同步執(zhí)行的??芍厝腈i使用時(shí),當(dāng)一個(gè)線程請(qǐng)求一個(gè)對(duì)象鎖時(shí),再次請(qǐng)求該鎖是可以立即得到的。出現(xiàn)異常,會(huì)自動(dòng)釋放鎖同步方法與同步代碼塊作用于整個(gè)方法,可能引起方法執(zhí)行效率下降。
synchronize可以在多個(gè)線程操作同一個(gè)成員變量或者方法時(shí),實(shí)現(xiàn)同步(或者互斥)的效果。
synchronized可以作用于方法,以及方法內(nèi)部的代碼塊。
//1 synchronized void method(){} //2 static synchronized void method(){} //3 synchronized void method(){ synchronized(鎖對(duì)象){ } } //4 static synchronized void method(){ synchronized(鎖對(duì)象){ } }鎖對(duì)象
那么在上面的示例中,它們分別持有的鎖對(duì)象是誰(shuí)?
synchronized作用于非靜態(tài)方法以及非靜態(tài)方法內(nèi)部的代碼塊,持有的是當(dāng)前類(lèi)的對(duì)象的鎖,并且是同一個(gè)鎖。作用于靜態(tài)方法及其內(nèi)部的代碼塊,持有的是當(dāng)前類(lèi)的Class對(duì)象的鎖,并且和非靜態(tài)方法不是同一個(gè)鎖。
通過(guò)代碼來(lái)驗(yàn)證。
public class SynchronizedTest { private synchronized void test1(){ for (int x = 0; x < 5; x++) { System.out.println("test1---"+x); } } private void test2(){ synchronized(this) { for (int x = 0; x < 5; x++) { System.out.println("---test2---"+x); } } } private static synchronized void test3(){ for (int x = 0; x < 5; x++) { System.out.println("------test3---"+x); } } private static void test4(){ synchronized (SynchronizedTest.class){ for (int x = 0; x < 5; x++) { System.out.println("---------test4---"+x); } } } public static void main(String[] args) { SynchronizedTest synchronizedTest = new SynchronizedTest(); new Thread(new Runnable() { @Override public void run() { synchronizedTest.test1(); } }).start(); new Thread(new Runnable() { @Override public void run() { synchronizedTest.test2(); } }).start(); new Thread(new Runnable() { @Override public void run() { test3(); } }).start(); new Thread(new Runnable() { @Override public void run() { test4(); } }).start(); } }
執(zhí)行結(jié)果
test1---0 ------test3---0 test1---1 ------test3---1 test1---2 test1---3 ------test3---2 test1---4 ------test3---3 ------test3---4 ---test2---0 ---test2---1 ---test2---2 ---test2---3 ---test2---4 ---------test4---0 ---------test4---1 ---------test4---2 ---------test4---3 ---------test4---4
test1和test2不會(huì)交叉執(zhí)行,test3和test4也不會(huì)交叉執(zhí)行。非靜態(tài)方法以及方法內(nèi)部的代碼塊持有的是同一個(gè)對(duì)象鎖,它們是同步執(zhí)行的。靜態(tài)方法和內(nèi)部的代碼塊持有的是當(dāng)前類(lèi)的Class對(duì)象鎖,它們是同步執(zhí)行的。而靜態(tài)方法和非靜態(tài)方法持有的不是同一個(gè)鎖,它們是異步的。
String作為鎖字符串常量作為鎖,會(huì)有什么結(jié)果?
final String a = "100"; final String b = "100"; new Thread(new Runnable() { @Override public void run() { synchronized (a){ while (true){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } } },"thread-a").start(); new Thread(new Runnable() { @Override public void run() { synchronized (b){ while (true){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } } },"thread-b").start();
這里字符串a(chǎn)和b雖然是兩個(gè)對(duì)象,但是聲明b時(shí),會(huì)將字符串常量池中已存在的a的值直接賦給b。這樣a和b其實(shí)是一樣的。這樣線程thread-a和thread-b同時(shí)搶占同一個(gè)鎖,一旦一個(gè)線程搶到該鎖,另一個(gè)線程就再也獲取不到該鎖。
synchronized不具有繼承性自己覆蓋了父類(lèi)被synchronized修飾的方法,子類(lèi)方法如果需要同步性,也需要用synchronized修飾。
定義子類(lèi)Sub繼承自SynchronizedTest
class Sub extends SynchronizedTest{ @Override public void test2() { //super.test2(); for (int x = 0; x < 15; x++) { try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } }
然后開(kāi)啟兩個(gè)線程調(diào)用Sub的test2()方法。
final Sub sub = new Sub(); new Thread(new Runnable() { @Override public void run() { sub.test2(); } },"Sub---A").start(); new Thread(new Runnable() { @Override public void run() { sub.test2(); } },"Sub---B").start();
打印結(jié)果
Sub---A Sub---B Sub---A Sub---B Sub---A Sub---B Sub---A Sub---B
可見(jiàn)Sub的test2()方法并沒(méi)有同步性。也就是synchronized不能被繼承。
可重入鎖使用synchronized時(shí),當(dāng)一個(gè)線程請(qǐng)求一個(gè)對(duì)象鎖時(shí),再次請(qǐng)求該鎖是可以立即得到的。
public class SynchronizedTest { private synchronized void test1(){ for (int x = 0; x < 5; x++) { System.out.println("test1---"+x); } test2(); } public void test2(){ synchronized(this) { for (int x = 0; x < 5; x++) { System.out.println("---test2---"+x); } } } public static void main(String[] args) { SynchronizedTest synchronizedTest = new SynchronizedTest(); new Thread(new Runnable() { @Override public void run() { synchronizedTest.test1(); } }).start(); } }
在方法test1()中調(diào)用方法test2(),并不需要等待鎖,而是立即獲取到鎖。
把子類(lèi)Sub的test2()方法也改成synchronized修飾。并在其內(nèi)部調(diào)用父類(lèi)的test2()方法。能獲得鎖嗎?
class Sub extends SynchronizedTest{ @Override public synchronized void test2() { //調(diào)用父類(lèi)的同步方法 super.test2(); for (int x = 0; x < 15; x++) { try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } }
打印結(jié)果
---test2---0 ---test2---1 ---test2---2 ---test2---3 ---test2---4 Sub---A Sub---A Sub---A Sub---A
可以看到父類(lèi)方法執(zhí)行完畢,子類(lèi)的方法立即執(zhí)行??梢?jiàn),在子父類(lèi)的繼承關(guān)系中,也支持可重入鎖這個(gè)特性。
出現(xiàn)異常,會(huì)自動(dòng)釋放鎖 同步方法與同步代碼塊synchronized作用于整個(gè)方法,可能引起方法執(zhí)行效率下降。建議將方法內(nèi)部需要同步的代碼用synchronized修飾,也就是synchronized代碼塊。
多個(gè)同步鎖對(duì)象在一個(gè)類(lèi)中假如有多個(gè)同步方法,它們之間并不需要互斥。那么使用同一個(gè)鎖,會(huì)大大降低效率。可以定義多個(gè)同步鎖對(duì)象。
Object obj1 = new Object(); Object obj2 = new Object(); public void method1(){ synchronized(obj1){ } } public void method2(){ synchronized(obj2){ } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77669.html
摘要:轉(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)...
摘要:關(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...
摘要:本文對(duì)多線程基礎(chǔ)知識(shí)進(jìn)行梳理,主要包括多線程的基本使用,對(duì)象及變量的并發(fā)訪問(wèn),線程間通信,的使用,定時(shí)器,單例模式,以及線程狀態(tài)與線程組。源碼采用構(gòu)建,多線程這部分源碼位于模塊中。通知可能等待該對(duì)象的對(duì)象鎖的其他線程。 本文對(duì)多線程基礎(chǔ)知識(shí)進(jìn)行梳理,主要包括多線程的基本使用,對(duì)象及變量的并發(fā)訪問(wèn),線程間通信,lock的使用,定時(shí)器,單例模式,以及線程狀態(tài)與線程組。 寫(xiě)在前面 花了一周時(shí)...
摘要:一線程的基本概念單線程簡(jiǎn)單的說(shuō),單線程就是進(jìn)程中只有一個(gè)線程。多線程由一個(gè)以上線程組成的程序稱(chēng)為多線程程序。當(dāng)線程調(diào)用完方法進(jìn)入后會(huì)自動(dòng)釋放鎖,線程獲得鎖。 一、線程的基本概念 1.1 單線程 簡(jiǎn)單的說(shuō),單線程就是進(jìn)程中只有一個(gè)線程。單線程在程序執(zhí)行時(shí),所走的程序路徑按照連續(xù)順序排下來(lái),前面的必須處理好,后面的才會(huì)執(zhí)行。 Java示例: public class SingleThrea...
摘要:底層是是通過(guò)對(duì)象,對(duì)象有自己的對(duì)象頭,存儲(chǔ)了很多信息,其中一個(gè)信息標(biāo)示是被哪個(gè)線程持有。當(dāng)一個(gè)線程執(zhí)行的代碼出現(xiàn)異常時(shí),其所持有的鎖會(huì)自動(dòng)釋放。 前言 回顧前面: 多線程三分鐘就可以入個(gè)門(mén)了! Thread源碼剖析 多線程基礎(chǔ)必要知識(shí)點(diǎn)!看了學(xué)習(xí)多線程事半功倍 只有光頭才能變強(qiáng)! 本文章主要講的是Java多線程加鎖機(jī)制,有兩種: Synchronized 顯式Lock 不得不嘮...
摘要:當(dāng)一個(gè)線程持有重量級(jí)鎖時(shí),另外一個(gè)線程就會(huì)被直接踢到同步隊(duì)列中等待。 java代碼先編譯成字節(jié)碼,字節(jié)碼最后編譯成cpu指令,因此Java的多線程實(shí)現(xiàn)最終依賴于jvm和cpu的實(shí)現(xiàn) synchronized和volatile 我們先來(lái)討論一下volatile關(guān)鍵字的作用以及實(shí)現(xiàn)機(jī)制,每個(gè)線程看到的用volatile修飾的變量的值都是最新的,更深入的解釋就涉及到Java的內(nèi)存模型了,我們...
閱讀 3329·2021-11-24 09:39
閱讀 2852·2021-10-12 10:20
閱讀 1943·2019-08-30 15:53
閱讀 3104·2019-08-30 14:14
閱讀 2632·2019-08-29 15:36
閱讀 1162·2019-08-29 14:11
閱讀 1981·2019-08-26 13:51
閱讀 3447·2019-08-26 13:23