先说答案吧!
synchronized是可重入锁。
简单点说,可重入锁是:同一个线程重复请求由自己持有的锁对象时,可以请求成功而不会发生死锁。
在网上看见一个例子,感觉不错。我自己试了一下,可以证明synchronized是可重入锁
子类
package cn.org.test;
/**
* *************************************************************************
* <p/>
*
* @文件名称: ChildProgram.java
* @包 路 径: cn.org.test
* @版权所有:北京数字认证股份有限公司 (C) 2019
* @类描述:
* @版本: V1.0 @创建人:wangyangyang
* @创建时间:2019-8-24 18:03
*/
public class ChildProgram extends SuperProgram {
public static void main(String[] args) {
ChildProgram childProgram = new ChildProgram();
childProgram.childSomeSthing();
}
public synchronized void childSomeSthing (){
superDoSomeSthing();
System.out.println("child do Something");
}
@Override
public synchronized void superDoSomeSthing() {
System.out.println("child do Something");
super.superDoSomeSthing();
}
}
父类
package cn.org.test;
/**
* *************************************************************************
* <p/>
*
* @文件名称: SuperProgram.java
* @包 路 径: cn.org.test
* @版权所有:北京数字认证股份有限公司 (C) 2019
* @类描述:
* @版本: V1.0 @创建人:wangyangyang
* @创建时间:2019-8-24 18:03
*/
public class SuperProgram {
public synchronized void superDoSomeSthing (){
System.out.println("super,doing something");
}
}
执行结果:
child do Something
super,doing something
child do Something
Process finished with exit code 0
可以看到调用的三个方法均得到了执行。我们知道synchronized修饰普通方法时,使用的是对象锁,也就是ChildProgram对象。三个方法的锁都是ChildProgram对象。我们在子类中执行childSomeSthing方法时,获取了ChildProgram对象锁,然后在childSomeString时调用了重写父类的superDoSomeSthing方法,该方法的锁也是ChildProgram对象锁,然后在其中调用父类的superDoSomeSthing方法,该方法的锁也是ChildProgram对象锁。一个锁多次请求,而且都成功了,所以synchronized是可重入锁。
synchronized可重入锁的实现原理:
synchronized底层的实现原理是利用计算机系统的mutex Lock实现。每一个可重入锁都会关联一个线程ID和一个锁状态status。
当一个线程请求方法时,会去检查锁状态,如果锁状态是0,代表该锁没有被占用,直接进行CAS操作获取锁,将线程ID替换成自己的线程ID。如果锁状态不是0,代表有线程在访问该方法。此时,如果线程ID是自己的线程ID,如果是可重入锁,会将status自增1,然后获取到该锁,进而执行相应的方法。如果是非重入锁,就会进入阻塞队列等待。
释放锁时,可重入锁,每一次退出方法,就会将status减1,直至status的值为0,最后释放该锁。
释放锁时,非可重入锁,线程退出方法,直接就会释放该锁。
所以,从一定程度上来说,可重入锁可以避免死锁的发生。