Java并發(fā)編程中的synchronized解析
一:synchronized的作用范圍
synchronized可作用在普通的方法上,靜態(tài)方法上以及同步代碼塊上。以下,我將分別的對(duì)這三種情況做一個(gè)分析。
1:作用于普通方法上
public class Demo01 implements Runnable{ private int a; private synchronized void add() { a++; } @Override public void run() { for (int i = 0; i < 100; i++) { add(); } } public static void main(String[] args) { Demo01 demo01 =new Demo01(); Thread thread1 =new Thread(demo01); Thread thread2 =new Thread(demo01); thread1.start(); thread2.start(); } }
兩個(gè)線程對(duì)變量a各進(jìn)行100次的自加,在add方法上加了synchronized以達(dá)到線程間的同步效果。synchronized加在方法上,持有鎖的為該類的實(shí)例對(duì)象,即本例的demo01。所以兩個(gè)線程訪問同一個(gè)鎖對(duì)象會(huì)有互斥情況。 如果這里每個(gè)線程持有的為兩個(gè)不同的類實(shí)例,如下:
Demo01 demo01 =new Demo01(); Demo01 demo02 =new Demo01(); Thread thread1 =new Thread(demo01); //demo01 Thread thread2 =new Thread(demo02); //demo02
很明顯,這里在方法上加上synchronized來同步是不行的,因?yàn)檫@里是兩個(gè)new出來的不同的實(shí)例,如果想要通過的該類的不同實(shí)例來加鎖,我們可以通過下面的方式’。
2:作用于靜態(tài)方法上
我們知道靜態(tài)方法是歸屬于類所有的,同一個(gè)類的不同實(shí)例可以通過持有其類的相同Class來達(dá)到線程同步的效果。
如下:
public class Demo02 implements Runnable{ private static int a; private synchronized static void add() { a++; } @Override public void run() { for (int i = 0; i < 100; i++) { add(); } } public static void main(String[] args) { Demo02 demo01 =new Demo02(); Demo02 demo02 =new Demo02(); Thread thread1 =new Thread(demo01); Thread thread2 =new Thread(demo02); thread1.start(); thread2.start(); } }
創(chuàng)建線程使用的為同一個(gè)類的不同實(shí)例,調(diào)用靜態(tài)方法的時(shí)候,還是能夠達(dá)到線程同步的效果。因?yàn)榇藭r(shí)持有鎖對(duì)象的為類的Class字節(jié)碼。 除了上面兩種方式,還可以通過同步代碼塊的方式來進(jìn)行加鎖操作。
3:作用于同步代碼塊上
public class Demo03 implements Runnable{ private int a; private void add() { synchronized (Object.class){ a++; } } @Override public void run() { for (int i = 0; i < 100; i++) { add(); } } public static void main(String[] args) { Demo03 demo01 =new Demo03(); Demo03 demo02 =new Demo03(); Thread thread1 =new Thread(demo01); Thread thread2 =new Thread(demo02); thread1.start(); thread2.start(); } }
以上例子持有鎖對(duì)象的為Object的Class,這里應(yīng)該注意的是,在同步代碼塊中,只要能夠保證每個(gè)線程過來時(shí),是同一個(gè)對(duì)象即可(任何類型的同一個(gè)實(shí)例對(duì)象)
二:synchronized同步的原理
synchronized的實(shí)現(xiàn)原理中,同步代碼塊與同步方法,同步靜態(tài)方法是不一樣的。在同步代碼塊中,是通過現(xiàn)實(shí)的監(jiān)視器對(duì)象來標(biāo)識(shí)的。而同步方法和同步靜態(tài)是通過同步方法來標(biāo)識(shí)的。通過查看其源碼的字節(jié)碼文件,我們可以清楚的看到其原理。
同步代碼塊字節(jié)碼文件:
public void add(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: aload_0 1: dup 2: astore_1 3: monitorenter //持有鎖 4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 7: ldc #3 // String Aaa 9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 12: aload_1 13: monitorexit //釋放鎖 14: goto 22 17: astore_2 18: aload_1 19: monitorexit //釋放鎖 20: aload_2 21: athrow 22: return Exception table: from to target type 4 14 17 any 17 20 17 any LineNumberTable: line 6: 0 line 7: 4 line 8: 12 line 9: 22 LocalVariableTable: Start Length Slot Name Signature 0 23 0 this Lcom/sg/thread002/threadSynchronized/Demo04; StackMapTable: number_of_entries = 2 frame_type = 255 /* full_frame */ offset_delta = 17 locals = [ class com/sg/thread002/threadSynchronized/Demo04, class java/lang/Object ] stack = [ class java/lang/Throwable ] frame_type = 250 /* chop */ offset_delta = 4
可以看到同步代碼塊是直接顯示的執(zhí)行monitorenter和monitorexit ,JVM保證無論在什么情況下,執(zhí)行完同步的代碼塊,就會(huì)釋放鎖,所以會(huì)有兩個(gè)monitorexit,另一個(gè)則是保證在異常的時(shí)候也是能夠釋放掉鎖的。
同步普通方法與靜態(tài)方法:
public static synchronized void add(); descriptor: ()V //同步的標(biāo)識(shí) //ACC_PUBLIC :公共方法 //ACC_STATIC :靜態(tài)方法 //ACC_SYNCHRONIZED :同步方法標(biāo)識(shí) flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED Code: stack=2, locals=0, args_size=0 0: getstatic #2 // Field a:I 3: iconst_1 4: iadd 5: putstatic #2 // Field a:I 8: return LineNumberTable: line 7: 0 line 8: 8
這里可以看到在方法級(jí)別的同步是通過ACC_SYNCHRONIZED來標(biāo)識(shí)的。
到這里,關(guān)于synchronized就其使用的范圍以及原理做了一個(gè)分析,雖然說其是一個(gè)重量級(jí)的鎖,但是JDK在1.6的時(shí)候?qū)ζ溥M(jìn)行了優(yōu)化,引入的偏向鎖,提高其性能。于此同時(shí),synchronized也是一種可重入的鎖。
到此這篇關(guān)于Java并發(fā)編程中的synchronized解析的文章就介紹到這了,更多相關(guān)Java的synchronized解析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java基礎(chǔ)篇之對(duì)象數(shù)組練習(xí)
對(duì)象數(shù)組就是數(shù)組里的每個(gè)元素都是類的對(duì)象,賦值時(shí)先定義對(duì)象,然后將對(duì)象直接賦給數(shù)組就行了,這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)篇之對(duì)象數(shù)組練習(xí)的相關(guān)資料,需要的朋友可以參考下2024-03-03JAVA中關(guān)于Long類型返回前端精度丟失問題處理辦法
這篇文章主要介紹了后端JavaBean的id屬性從Long類型改為雪花算法后出現(xiàn)的精度丟失問題,解決方案包括將id字段類型改為字符串或使用Jackson序列化方式,需要的朋友可以參考下2024-11-11Java實(shí)現(xiàn)泡泡堂對(duì)戰(zhàn)版游戲的示例代碼
本文將利用Java制作經(jīng)典游戲《泡泡堂》,文中使用了MVC模式,分離了模型、視圖和控制器,使得項(xiàng)目結(jié)構(gòu)清晰易于擴(kuò)展,感興趣的可以了解一下2022-04-04Spring Cloud Feign 自定義配置(重試、攔截與錯(cuò)誤碼處理) 代碼實(shí)踐
這篇文章主要介紹了Spring Cloud Feign 自定義配置(重試、攔截與錯(cuò)誤碼處理) 實(shí)踐,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08可能是全網(wǎng)最詳細(xì)的springboot整合minio教程
MinIO是全球領(lǐng)先的對(duì)象存儲(chǔ)先鋒,在標(biāo)準(zhǔn)硬件上,讀/寫速度上高達(dá)183 GB/秒和171 GB/秒,下面這篇文章主要給大家介紹了關(guān)于springboot整合minio的相關(guān)資料,這個(gè)教程可能是全網(wǎng)最詳細(xì)的,需要的朋友可以參考下2022-06-06