synchronized 是 JAVA 关键字,用在方法或代码块。控制方法或代码同一时间只有一个线程执行,用来解决多线程同时访问出现的并发问题。
private synchronized boolean sellTicketsWithSyncMethod(int count) { if (totalTickets - count < 0) { return false; } else { totalTickets = totalTickets - count; return true; } } 复制代码
锁多个对象时,相互不干扰
private final Object LOCK = new Object(); private boolean sellTicketsWithSyncObject(int count) { synchronized (LOCK) { if (totalTickets - count < 0) { return false; } else { totalTickets = totalTickets - count; return true; } } } 复制代码
private boolean sellTicketsWithSyncObject(int count) { synchronized (this) { if (totalTickets - count < 0) { return false; } else { totalTickets = totalTickets - count; return true; } } } 复制代码
public static synchronized void staticMethod() { // todo } 复制代码
public Class SyncClass { private void method () { synchronized (SyncClass.class) { //todo } } } 复制代码
对象锁锁的是一个对象。不同对象下互不干扰。而类只有一个。
演示:
public class SynchronizedReentrantTest { int i = 0; public synchronized void method1() { method2(); } private synchronized void method2() { } private synchronized void reentrantSelf() { if (i == 0) { i++; reentrantSelf(); } } public static void main(String[] args) { SynchronizedReentrantTest instance = new SynchronizedReentrantTest(); // 重入其他方法 instance.method1(); // 自己重入自己 instance.reentrantSelf(); // 当然也可以重入其他类方法,这里不演示了。 } } 复制代码
javac xxxx.java javap -c -p -v xxxx.class
以下是截取的部分源码:
public class SynchronizedMethodDif { //... // 作用于对象上 private static final Object LOCK = new Object(); private static boolean sellTicketsWithSyncObject(int count) { synchronized (LOCK) { if (totalTickets - count < 0) { return false; } else { totalTickets = totalTickets - count; return true; } } } // 作用于 static 方法上 private static synchronized void soutStatic() { System.out.println(Thread.currentThread().getName() + " is sleeping "); try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " : 1"); System.out.println(Thread.currentThread().getName() + " finish sleeping "); } // 作用于普通方法上 private synchronized void soutNonStatic() { System.out.println(Thread.currentThread().getName() + " is sleeping "); try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " : 1"); System.out.println(Thread.currentThread().getName() + " finish sleeping "); } //... } 复制代码
// 作用于对象上 private static boolean sellTicketsWithSyncObject(int); descriptor: (I)Z flags: (0x000a) ACC_PRIVATE, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: getstatic #10 // Field LOCK:Ljava/lang/Object; 3: dup 4: astore_1 5: monitorenter ... 16: monitorexit 17: ireturn ... 27: aload_1 28: monitorexit 29: ireturn ... 31: aload_1 32: monitorexit 33: aload_2 34: athrow ... frame_type = 75 /* same_locals_1_stack_item */ stack = [ class java/lang/Throwable ] ... // 作用于 static 上 private static synchronized void soutStatic(); descriptor: ()V flags: (0x002a) ACC_PRIVATE, ACC_STATIC, ACC_SYNCHRONIZED Code: stack=2, locals=1, args_size=0 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; ... frame_type = 4 /* same */ ... // 作用于 普通方法 上 private synchronized void soutNonStatic(); descriptor: ()V flags: (0x0022) ACC_PRIVATE, ACC_SYNCHRONIZED Code: stack=2, locals=2, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; ... frame_type = 4 /* same */ 复制代码
通过 monitor 来控制,使用monitorenter 和 monitorexit 指令控制线程进出。
是一个监视器,执行是依赖于对象的锁计数
monitorenter
:每个对象都是一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下: monitorexit
:执行monitorexit的线程必须是objectref所对应的monitor的所有者。指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。