Java并發(fā)編程中的synchronized解析
一:synchronized的作用范圍
synchronized可作用在普通的方法上,靜態(tài)方法上以及同步代碼塊上。以下,我將分別的對這三種情況做一個分析。
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(); } }
兩個線程對變量a各進行100次的自加,在add方法上加了synchronized以達到線程間的同步效果。synchronized加在方法上,持有鎖的為該類的實例對象,即本例的demo01。所以兩個線程訪問同一個鎖對象會有互斥情況。 如果這里每個線程持有的為兩個不同的類實例,如下:
Demo01 demo01 =new Demo01(); Demo01 demo02 =new Demo01(); Thread thread1 =new Thread(demo01); //demo01 Thread thread2 =new Thread(demo02); //demo02
很明顯,這里在方法上加上synchronized來同步是不行的,因為這里是兩個new出來的不同的實例,如果想要通過的該類的不同實例來加鎖,我們可以通過下面的方式’。
2:作用于靜態(tài)方法上
我們知道靜態(tài)方法是歸屬于類所有的,同一個類的不同實例可以通過持有其類的相同Class來達到線程同步的效果。
如下:
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)建線程使用的為同一個類的不同實例,調用靜態(tài)方法的時候,還是能夠達到線程同步的效果。因為此時持有鎖對象的為類的Class字節(jié)碼。 除了上面兩種方式,還可以通過同步代碼塊的方式來進行加鎖操作。
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(); } }
以上例子持有鎖對象的為Object的Class,這里應該注意的是,在同步代碼塊中,只要能夠保證每個線程過來時,是同一個對象即可(任何類型的同一個實例對象)
二:synchronized同步的原理
synchronized的實現(xiàn)原理中,同步代碼塊與同步方法,同步靜態(tài)方法是不一樣的。在同步代碼塊中,是通過現(xiàn)實的監(jiān)視器對象來標識的。而同步方法和同步靜態(tài)是通過同步方法來標識的。通過查看其源碼的字節(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í)行完同步的代碼塊,就會釋放鎖,所以會有兩個monitorexit,另一個則是保證在異常的時候也是能夠釋放掉鎖的。
同步普通方法與靜態(tài)方法:
public static synchronized void add(); descriptor: ()V //同步的標識 //ACC_PUBLIC :公共方法 //ACC_STATIC :靜態(tài)方法 //ACC_SYNCHRONIZED :同步方法標識 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
這里可以看到在方法級別的同步是通過ACC_SYNCHRONIZED來標識的。
到這里,關于synchronized就其使用的范圍以及原理做了一個分析,雖然說其是一個重量級的鎖,但是JDK在1.6的時候對其進行了優(yōu)化,引入的偏向鎖,提高其性能。于此同時,synchronized也是一種可重入的鎖。
到此這篇關于Java并發(fā)編程中的synchronized解析的文章就介紹到這了,更多相關Java的synchronized解析內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java實現(xiàn)泡泡堂對戰(zhàn)版游戲的示例代碼
本文將利用Java制作經(jīng)典游戲《泡泡堂》,文中使用了MVC模式,分離了模型、視圖和控制器,使得項目結構清晰易于擴展,感興趣的可以了解一下2022-04-04Spring Cloud Feign 自定義配置(重試、攔截與錯誤碼處理) 代碼實踐
這篇文章主要介紹了Spring Cloud Feign 自定義配置(重試、攔截與錯誤碼處理) 實踐,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08可能是全網(wǎng)最詳細的springboot整合minio教程
MinIO是全球領先的對象存儲先鋒,在標準硬件上,讀/寫速度上高達183 GB/秒和171 GB/秒,下面這篇文章主要給大家介紹了關于springboot整合minio的相關資料,這個教程可能是全網(wǎng)最詳細的,需要的朋友可以參考下2022-06-06