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 是使用monitorenter 和monitorexit 指令来实现的,同步方法需要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方法还需要一把锁吗?
答案是否定的,因为锁的持有者是线程,而不是调用者 。当线程已经有了子类的实例对象的锁之后,当再次需要的时候,可以继续开锁进去的。
释放锁的时机
- 当方法执行完毕之后会自动释放锁,无需任何操作。
- 当一个线程的代码出现异常时,其所持有的锁会自动释放