可重入锁
指在同一个线程在外层方法获取锁的时候,进入内层方法会自动获取锁
为了避免死锁的发生,JDK中基本都是可重入锁
下面我们来测试一下synchronized和 java.util.concurrent.lock.ReentrantLock 锁的可重入性
测试synchronized加锁可重入性
class TestSychronizedReentrant{
public static void main(String[] args) {
new Thread(new SychronizedReentrant()).start();
}
}
class SychronizedReentrant implements Runnable{
private Object object = new Object();
/**
* 方法一调用方法二
*/
public void method1(){
synchronized (object){
System.out.println(Thread.currentThread().getName()+ " method1 ");
method2();
}
}
/**
方法二,打印获取obj锁
如果是同一线程,锁不可以重入,method2需要等待method1释放锁
*/
public void method2(){
synchronized (object){
System.out.println(Thread.currentThread().getName()+ " method2 ");
}
}
@Override
public void run() {
method1();
}
}
Thread-0 method1
Thread-0 method2
测试ReentrantLock的可重入性
class TestLockReentrant{
public static void main(String[] args) {
new Thread(new LockReentrant()).start();
}
}
class LockReentrant implements Runnable{
private final Lock lock = new ReentrantLock();
/**
* 方法一:调用方法二
*/
public void method1(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "method1");
method2();
}finally {
lock.unlock();
}
}
/**
* 打印获取object锁
*/
public void method2(){
lock.lock();//获取锁
try {
System.out.println(Thread.currentThread().getName() + "method2");
}finally {
lock.unlock();
}
}
@Override
public void run() {
//线程启动,调用方法1
method1();
}
}
Thread-0method1
Thread-0method2
我们发现方法二都能获取到锁,所以说他们都是可重入锁
测试不可重入锁
我们在JDK中并没有找到可重入锁,所以可以考虑自己实现.有两种实现方式:通过synchronized和wait notify实现;通过CAS+自旋方式实现
synchronized wait notify 方式实现
public class NonReentrantLockBywait {
//是否被锁
private volatile boolean locked = false;
//加锁
public synchronized void lock(){
while (locked){
try {
wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
//加锁成功 locked设置为true
locked = true;
}
//释放锁
public synchronized void unLock(){
locked = false;
notify();
}
}
通过CAS + 自旋方式实现
class NonReentrantLockByCAS {
private AtomicReference<Thread> lockedThread = new AtomicReference<Thread>();
public void lock() {
Thread t = Thread.currentThread();
//当 lockedThread 持有引用变量为 null 时,设置 lockedThread 持有引用为 当前线程变量
while (!lockedThread.compareAndSet(null, t)) {
//自旋,空循环,等到锁被释放
}
}
public void unlock() {
//如果是本线程锁定的,可以成功释放锁
lockedThread.compareAndSet(Thread.currentThread(), null);
}
}
测试类
class TestLockNonReentrant{
public static void main(String[] args) {
new Thread(new LockNonReentrant()).start();
}
}
class LockNonReentrant implements Runnable {
// private final NonReentrantLockBywait lock = new NonReentrantLockByWait();
private final NonReentrantLockByCAS lock = new NonReentrantLockByCAS();
/**
* 方法1,调用方法2
*/
public void method1() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " method1()");
method2();
} finally {
lock.unlock();
}
}
/**
* 方法2,打印前获取 obj 锁
* 如果同一线程,锁不可重入的话,method2 需要等待 method1 释放 obj 锁
*/
public void method2() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " method2()");
} finally {
lock.unlock();
}
}
@Override
public void run() {
//线程启动 执行方法1
method1();
}
}
测试结果都是在method1,调用method2的时候,导致了死锁,线程一直等待或者自旋下去