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 的所有权。