Synchronized底层原理和使用

263 阅读2分钟
synchronized的作用

sychronized 可以保证方法或者代码块在运行的过程中,同一时刻只有一个方法进入到临界区,同时可以保证共享变量的可见性。

synchronized底层实现的原理

先看一段简单的synchronized代码

public class Test {

    public synchronized void test1() {

    }

    public void test2() {
        synchronized (this) {

        }
    }

}

然后利用javap反编译工具查看生成的class文件

  
  public com.oyo.thread.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public synchronized void test1();
    Code:
       0: return

  public void test2();
    Code:
       0: aload_0
       1: dup
       2: astore_1
       3: monitorenter
       4: aload_1
       5: monitorexit
       6: goto          14
       9: astore_2
      10: aload_1
      11: monitorexit
      12: aload_2
      13: athrow
      14: return
    Exception table:
       from    to  target type
           4     6     9   any
           9    12     9   any
}

可以看出同步代码块的synchronized 是使用monitorentermonitorexit 指令来实现的,同步方法需要JVM底层实现,依靠的是方法修饰符 上的ACC_SYNCHRONIZED实现。

synchronized的使用

synchronized一般我们用来修饰三种东西:

  • 修饰普通方法
  • 使用静态方法
  • 修饰代码块
重入锁

看下面第一段代码:

/**
 * 父类
 */
@Data
public class ThreadParentTest {
    public synchronized void doSomething() {
        System.out.println(toString() +": parent doSomething ");
    }

    public static void main(String[] args) {
        ThreadChildTest threadChildTest = new ThreadChildTest();
        threadChildTest.doSomething();

    }
}

/**
 * 子类
 */
@Data
class ThreadChildTest extends ThreadParentTest {

    public synchronized void doSomething() {
        System.out.println(toString() + ": child doSomething");
        super.doSomething();
    }
}
  • 当线程进入子类的doSomething时,通过synchronized 拿到了锁。
  • 然后调用父类的doSomething方法,也是通过synchronized拿到了锁。
  • 由于子类实例对象的锁还有释放,那我们进入到父类的doSomething方法还需要一把锁吗?

答案是否定的,因为锁的持有者是线程,而不是调用者 。当线程已经有了子类的实例对象的锁之后,当再次需要的时候,可以继续开锁进去的。

释放锁的时机
  • 当方法执行完毕之后会自动释放锁,无需任何操作。
  • 当一个线程的代码出现异常时,其所持有的锁会自动释放