我们知道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 locks
Object 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 2
10 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 loop
12 iconst_0 // Push constant int 0
13 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 loop
18 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
19 getfield #4
22 arraylength // Pop array ref, push int array length
23 iconst_1 // Push constant int 1
24 isub // Pop two ints, subtract, push int result
25 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
// intArray
31 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
// intArray
40 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
// intArray
46 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
// intArray
54 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 i
62 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 to
68 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 object
73 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 object
76 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 loop
13 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
14 getfield #4
17 arraylength // Pop array ref, push int array length
18 iconst_1 // Push constant int 1
19 isub // Pop two ints, subtract, push int result
20 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
// intArray
24 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
// intArray
32 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
// intArray
37 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
// intArray
44 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 i
51 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 to
56 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而使用它,只有考虑设计线程安全类的时候才用。因为加锁解锁都是需要开销的,能不用就不用。