synchronized底层实现

628 阅读2分钟

简介

synchronized关键字是Java中用来解决资源同步的一种方式,它能够保证被它修饰的代码块或者方法在任意时刻都只能由一个线程被访问。

synchronized关键字的底层实现

我们通过书写一段代码然后去反编译它的字节码文件

public class SynchronizedDemo3 {
    public void method() {
        synchronized (this) {
            System.out.println("SynchronizedDemo3");
        }
    }
}

图片.png

从字节码文件中可以看到,底层是通过monitorenter和monitorexit指令来实现的。

每一个对象都有一个监视器锁(monitor),我们去对资源的一个竞争其实归根到底是对monitor的一个争夺,当线程通过monitorenter执行尝试去获取monitor的所有权时,如果monitor的进入数为0,那么该线程则进入monitor并将进入数设置为1,此时该线程则用有了monitor的所有权,如果该线程再次去访问时,此时进入数则为继续+1。当另外的线程要尝试获取monitor的所有权时,发现此时monitor的进入数大于0,则说明此时被别的线程占用了,就会被阻塞,拥有monitor线程通过执行monitorexit指令,会将monitor的进入数-1,当进入数减到0时,此时说明该线程不再是monitor的所有者,它会去唤醒被阻塞的线程再去尝试获取monitor。

我们可以看到上面的字节码文件出现了两个monitorexit,第一个为正常退出锁,第二个为发生异常退出锁。

为什么说synchronized是重量级锁呢?
因为监视器锁的底层涉及到操作系统的互斥量,Java的线程其实归根结底是映射到操作系统上面的线程的,此时当我们去阻塞或者唤醒一个线程的时候,都需要操作系统的用户态和内核态的一个切换,这个过程是很消耗时间和资源的。

synchronized和lock的区别

  1. lock是个接口,synchronized是Java内置的关键字;
  2. 发生异常时,lock会发生死锁现象(需要在finally中去解锁),而synchronized不会导致死锁;
  3. lock需要手动释放锁,synchronized会自动释放锁;
  4. lock可以知道有没有成功获取得到锁,而synchronized则无法知道;
  5. lock可以提高多个线程的读操作(读写锁)。

synchronized和volatile的区别

  1. volatile强调的是变量在多个线程的可见性;而synchronized则是强调对个线程在访问资源的一个同步性;
  2. volatile用于修饰变量,而synchronized可以用于修饰方法和代码块;
  3. 多个线程访问volatile不会发生阻塞,而synchronized会发生阻塞;
  4. volatile不能保证原子性,而synchronized可以保证原子性。