上一篇文章博主简单介绍了synchronized的几种使用方法,下面来总结一下:
1.synchronized同步代码块:
- synchronized(this):锁当前对象
- synchronized(类.class):锁当前对象的class对象,即锁住类。相当于全局锁,锁住了一整块代码
- synchronized(指定对象):锁指定对象
2.synchronized同步方法:
- 普通方法+synchronized:锁的是调用该方法的当前对象
- 静态方法+synchronized:全局锁,锁住了一整块代码
3.synchronized的底层实现:
synchronized的底层实现其实就是monitor机制,monitor就是一个对象的监听器,只有获取到monitor之后才能让同步代码块继续向下执行,否则只能等待。
(1) 同步代码块:
- 进入同步代码块后,首先要执行monitorenter指令,退出时执行monitorexit指令。
- 通常一个monitorenter指令包含多个monitorexit指令,这是为了保证线程所获得的锁不论是在正常路径,还是在异常路径都能被正常解锁。
(2) 同步方法:
-
当使用synchronized来标记方法时,编译后字节码中方法的访问标记多了一个ACC_SYNCHRONIZED。
-
这个标记表示:进入该方法时,JVM要进行monitorenter操作;
退出该方法时,无论是否正常退出,都要进行monitorexit操作。 -
当执行monitorenter指令时,若目标锁对象的monitor计数器值为0,表示当前锁没有被任何线程所拿到,此时将当前线程置为持有锁的线程,并让monitor计数器的值加一;
-
若monitor计数器的值不为0,说明此时的锁已经被线程持有了:
a. 判断持有锁的线程是不是当前线程,如果是当前线程,则再次将计数器的值加一,这就是锁的可重入性。
b. 若不是,则要进行等待,直到锁被释放。 -
当执行monitorexit指令时,monitor计数器的值会减一;若计数器的值减为0,则说明锁已经被释放了。
4.锁的特性与实现:
- 锁的互斥性:即同一时刻只能有一个线程取得对象的monitor,从而保证同步。
class ReThread implements Runnable{
@Override
public void run() {
synchronized (this){
test11();
test2();
}
}
public void test11(){
while(true){
}
}
public synchronized void test2(){
System.out.println(Thread.currentThread().getName()+"_test2 Running");
}
}
public class ReTest {
public static void main(String[] args) {
ReThread reThread = new ReThread();
Thread thread = new Thread(reThread);
thread.start();
}
}
此时,test11()拿到monitor对象后,一直没有释放锁,所以test2()处于等待状态,直到test11()释放锁。可见,同一时刻只能有一个线程可以拿到锁。
- 锁的可重入性:即当一个线程拿到锁以后可以"为所欲为",可以继续拿到下一把锁。
class ReThread implements Runnable{
@Override
public void run() {
synchronized (this){
test1();
}
}
public synchronized void test1(){
System.out.println(Thread.currentThread().getName()+"_test1 Running");
test2();
System.out.println(Thread.currentThread().getName()+"_test1 End");
}
public synchronized void test2(){
System.out.println(Thread.currentThread().getName()+"_test2 Running");
System.out.println(Thread.currentThread().getName()+"_test2 End");
}
}
public class ReTest {
public static void main(String[] args) {
ReThread reThread = new ReThread();
Thread thread = new Thread(reThread);
thread.start();
}
}
结果如下:
可见,当test1()拿到锁以后,可以继续拿到下一个锁。