標籤:jvm java synchronized method synchronized block
我們知道synchronized有兩種:同步方法(synchronized method)和同步語句塊(synchronized block)。那麼這兩種有什麼區別以及優缺點呢?
SynchronizedMethod:
優點:代碼簡單清晰;易於維護
缺點:同步粒度過大,不利於並發;不夠靈活,預設用本對象或者本類鎖同步
Synchronizedblock :
優點:靈活,可以使用任意對象鎖;同步粒度小,並發度更高
缺點:代碼更複雜,不易於維護
對比:
Synchronized method:
預設鎖住的是this(修飾非靜態方法)或者.class(修飾靜態方法)。一旦整個對象被鎖住,那麼對象裡的其他同步方法將無法訪問,這樣可能導致本來不相關的方法執行的並發度降低。尤其是當同步的方法很大,但其中只有很小一塊訪問共用資料的代碼,這個時候用同步塊更合適。
synchronized void foo() { ...}void foo() { synchronized (this) { ... }}
public class MyClass { // locks MyClass.class public static synchronized void foo() {// do something } // similar public static void foo() { synchronized(MyClass.class) {// do something } }}
Synchronized block:
可以使用任意對象的鎖來鎖住同步塊,非常靈活。同步粒度自己調整。
// Using specific locksObject inputLock = new Object();Object outputLock = new Object();private void someInputRelatedWork() { synchronize(inputLock) { ... } }private void someOutputRelatedWork() { synchronize(outputLock) { ... }}
兩個方法可以同時執行,只要兩個線程分別獲得一個鎖(一個活的輸出鎖,一個獲得輸入鎖),如果改成同步方法,就沒有辦法同時執行了。而且可以根據自己的需要來決定用不同的鎖鎖住不同的代碼塊來實現同步。
例如下面的程式:修改List 的線程並不會阻塞修改Map的線程
private List<Foo> myList = new ArrayList<Foo>();private Map<String,Bar) myMap = new HashMap<String,Bar>();public void put( String s, Bar b ) { synchronized( myMap ) { myMap.put( s,b ); // then some thing that may take a while like a database access or RPC or notifying listeners }}public void hasKey( String s, ) { synchronized( myMap ) { myMap.hasKey( s ); }}public void add( Foo f ) { synchronized( myList ) { myList.add( f );// then some thing that may take a while like a database access or RPC or notifying listeners }}public Thing getMedianFoo() { Foo med = null; synchronized( myList ) { Collections.sort(myList); med = myList.get(myList.size()/2); } return med;}
闡述兩種方法的並發度:
JVM對Synchronized method 和Synchronizedblock在指令集上的支援;
Opcode |
Operand(s) |
Description |
monitorenter |
none |
pop objectref, acquire the lock associated with objectref |
monitorexit |
none |
pop objectref, release the lock associated with objectref |
Synchronized block
class KitchenSync { private int[] intArray = new int[10]; void reverseOrder() { synchronized (this) { int halfWay = intArray.length / 2; for (int i = 0; i < halfWay; ++i) { int upperIndex = intArray.length - 1 - i; int save = intArray[upperIndex]; intArray[upperIndex] = intArray[i]; intArray[i] = save; } } } // ...}
編譯後的位元組碼:
// First place the reference to the object to lock into local// variable 1. This local variable will be used by both the// monitorenter and monitorexit instructions. 0 aload_0 // Push local var 0 (the this reference) 1 astore_1 // Store into local var 1// Now acquire the lock on the referenced object // Push local var 1 (the this reference; the 2 aload_1 // object to lock) // Pop reference, acquire the lock 3 monitorenter // on referenced object// The code of the synchronized block begins here. A thread will not// execute the next instruction, aload_0, until a lock has been// successfully acquired on the this reference above. 4 aload_0 // Push the object ref at loc var 0 (the this ref) // Pop object ref, push ref to instance variable // intArray 5 getfield #4 8 arraylength // Pop array ref, push int array length 9 iconst_2 // Push constant int 210 idiv // Pop two ints, divide, push int result // Pop int into local var 3:11 istore_3 // int halfway = intArray.length/2;// This is the start of the code for the for loop12 iconst_0 // Push constant int 013 istore 4 // Pop into local var 2: int i = 0;15 goto 65 // Jump to for loop condition check// This is the start of the body of the for loop18 aload_0 // Push the object ref at loc var 0 (the this ref) // Pop object ref, push ref to instance variable // intArray19 getfield #4 22 arraylength // Pop array ref, push int array length23 iconst_1 // Push constant int 124 isub // Pop two ints, subtract, push int result25 iload 4 // Push int at local var 4 (i)27 isub // Pop two ints, subtract, push int result // Pop int into local var 5:28 istore 5 // int upperindex = intArray.length - 1 - i;30 aload_0 // Push the object ref at loc var 0 (the this ref) // Pop object ref, push ref to instance variable // intArray31 getfield #4 34 iload 5 // Push int at local var 5 (upperIndex)36 iaload // Pop index, arrayref, push int at arrayref[index] // Pop into local var 6:37 istore 6 // int save = intArray[upperIndex];39 aload_0 // Push the object ref at loc var 0 (the this ref) // Pop object ref, push ref to instance variable // intArray40 getfield #4 43 iload 5 // Push int at local var 5 (upperIndex)45 aload_0 // Push the object ref at loc var 0 (the this ref) // Pop object ref, push ref to instance variable // intArray46 getfield #4 49 iload 4 // Push int at local var 4 (i)51 iaload // Pop index, arrayref, push int at arrayref[index] // Set arrayref[index] = value:52 iastore // intArray[upperIndex] = intArray[i];53 aload_0 // Push the object ref at loc var 0 (the this ref) // Pop object ref, push ref to instance variable // intArray54 getfield #4 57 iload 4 // Push int at local var 4 (i)59 iload 6 // Push int at local var 6 (save) // Set arrayref[index] = value:61 iastore // intArray[i] = save;// The body of the for loop is now done, this instruction does// the incrementing of the loop variable i62 iinc 4 1 // Increment by 1 int at local var 4: ++i;// This is the for loop condition check:65 iload 4 // Push int at local var 4 (i)67 iload_3 // Push int at local var 3 (halfway) // Pop two ints, compare, jump if less than to68 if_icmplt 18 // top of for loop body: for (; i < halfway;)// The code of the synchronized block ends here// The next two instructions unlock the object, making it available// for other threads. The reference to the locked object was stored// in local variable 1 above.71 aload_1 // Push local var 1 (the this reference)72 monitorexit // Pop ref, unlock object73 return // return normally from method// This is a catch clause for any exception thrown (and not caught// from within the synchronized block. If an exception is thrown,// the locked object is unlocked, making it available for other// threads.74 aload_1 // Push local var 1 (the this reference)75 monitorexit // Pop ref, unlock object76 athrow // rethrow the same exception// The exception table shows the "catch all" clause covers the// entire synchronized block, from just after the lock is acquired// to just before the lock is released.Exception table:from to target type 4 71 74 any
Synchronized method
class HeatSync { private int[] intArray = new int[10]; synchronized void reverseOrder() { int halfWay = intArray.length / 2; for (int i = 0; i < halfWay; ++i) { int upperIndex = intArray.length - 1 - i; int save = intArray[upperIndex]; intArray[upperIndex] = intArray[i]; intArray[i] = save; } } // ...}
編譯後的位元組碼:
0 aload_0 // Push the object ref at loc var 0 (the this ref) // Pop object ref, push ref to instance variable // intArray 1 getfield #4 4 arraylength // Pop array ref, push int array length 5 iconst_2 // Push constant int 2 6 idiv // Pop two ints, divide, push int result // Pop int into local var 1: 7 istore_1 // int halfway = intArray.length/2;// This is the start of the code for the for loop 8 iconst_0 // Push int constant 0 9 istore_2 // Pop into local var 2: int i = 0;10 goto 54 // Jump to for loop condition check// This is the start of the body of the for loop13 aload_0 // Push the object ref at loc var 0 (the this ref) // Pop object ref, push ref to instance variable // intArray14 getfield #4 17 arraylength // Pop array ref, push int array length18 iconst_1 // Push constant int 119 isub // Pop two ints, subtract, push int result20 iload_2 // Push int at local var 2 (i)21 isub // Pop two ints, subtract, push int result // Pop int into local var 3:22 istore_3 // int upperindex = intArray.length - 1 - i;23 aload_0 // Push the object ref at loc var 0 (the this ref) // Pop object ref, push ref to instance variable // intArray24 getfield #4 27 iload_3 // Push int at local var 3 (upperIndex)28 iaload // Pop index, arrayref, push int at arrayref[index] // Pop into local var 4:29 istore 4 // int save = intArray[upperIndex];31 aload_0 // Push the object ref at loc var 0 (the this ref) // Pop object ref, push ref to instance variable // intArray32 getfield #4 35 iload_3 // Push int at local var 3 (upperIndex)36 aload_0 // Push the object ref at loc var 0 (the this ref) // Pop object ref, push ref to instance variable // intArray37 getfield #4 40 iload_2 // Push int at local var 2 (i)41 iaload // Pop index, arrayref, push int at arrayref[index] // Pop value, index, and arrayref, // Set arrayref[index] = value:42 iastore // intArray[upperIndex] = intArray[i];43 aload_0 // Push the object ref at loc var 0 (the this ref) // Pop object ref, push ref to instance variable // intArray44 getfield #4 47 iload_2 // Push int at local var 2 (i)48 iload 4 // Push int at local var 4 (save) // Pop value, index, and arrayref, // Set arrayref[index] = value:50 iastore // intArray[i] = save;// The body of the for loop is now done, this instruction does// the incrementing of the loop variable i51 iinc 2 1 // Increment by 1 int at local var 2: ++i;// This is the for loop condition check:54 iload_2 // Push int at local var 2 (i)55 iload_1 // Push int at local var 1 (halfway) // Pop two ints, compare, jump if less than to56 if_icmplt 13 // top of for loop body: for (; i < halfway;)59 return // return (void) from method
比較上面的位元組碼會發現,同步方法的位元組碼比同步塊的位元組碼更更高效,指令數更少;synchronized method沒有進入和離開監視器的代碼,且編譯器也不為heatSync的方法建立異常表。
因此,Synchronized method 和synchronized block不是絕對誰比誰好,具體情況具體分析。最後要說的就是,不要為了用Synchronized而使用它,只有考慮設計安全執行緒類的時候才用。因為加鎖解鎖都是需要開銷的,能不用就不用。
Java中synchronized關鍵字實現同步(二)