原理
synchronized是JVM层面的锁,是一种重量级的锁。synchronized可以同步方法和代码块。
public class Synchronized {
public static void main(String[] args) {
// 对Synchronized Class对象进行加锁
synchronized (Synchronized.class) {
}
// 静态同步方法,对Synchronized Class对象进行加锁
m();
}
public static synchronized void m() {
}
}
执行javap - v Synchronized
public static void main(java.lang.String[]);
// 方法修饰符,表示:public staticflags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: ldc #1 // class com/murdock/books/multithread/book/Synchronized
2: dup
3: monitorenter // monitorenter:监视器进入,获取锁
4: monitorexit // monitorexit:监视器退出,释放锁
5: invokestatic #16 // Method m:()V
8: return
public static synchronized void m();
// 方法修饰符,表示: public static synchronized
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=0, locals=0, args_size=0
0: return
方法级别的同步是隐式的,无需通过字节码指令来控制,它依靠的是方法表里的ACC_SYNCHRONIZED标志(什么是方法表和标志?),当方法调用时,调用指令会检查方法的ACC_SYNCHRONIZED是否被设置了,如果被设置了执行线程首先需要持有管程才能执行方法,执行后或异常时释放管程。
而代码块级别的同步依靠的是monitorenter和monitorexit指令,这两个指令总是成对执行的,在程序异常时编译器会生成一个异常处理器来执行monitorexit指令。
无论采用哪种方式,都是对一个对象的监视器或叫做管程(Monitor)进行获取,这个过程是排他的,也就是同一时刻只可以有一个线程获取到有synchronized保护对象的监视器。获取不到的线程会阻塞在同步方法或同步块的入口处,进入BLOCKED阻塞状态。这里要区别一下阻塞状态和等待状态,使用Object的wait方法后会进入等待队列,notify后唤醒线程从等待队列移入到阻塞(同步)队列。线程正常结束或者异常释放monitor。
以下是对象,对象的监视器,同步队列以及执行线程的关系