可重入锁与不可重入锁之间的区别与性能差异?

可重入锁

指在同一个线程在外层方法获取锁的时候,进入内层方法会自动获取锁。

为了避免死锁的发生,JDK 中基本都是可重入锁。

 

下面我们来测试一下 synchronized 和  java.util.concurrent.lock.ReentrantLock 锁的可重入性

  • 测试 synchronized 加锁 可重入性
package constxiong.concurrency.a019;

/**
 * 测试 synchronized 加锁 可重入性
 * @author ConstXiong
 * @date 2019-09-20 15:55:27
 */
public class TestSynchronizedReentrant {
	
	public static void main(String[] args) {
		new Thread(new SynchronizedReentrant()).start();
	}
	
}

class SynchronizedReentrant implements Runnable {

	private final Object obj = new Object();
	
	/**
	 * 方法1,调用方法2
	 */
	public void method1() {
		synchronized (obj) {
			System.out.println(Thread.currentThread().getName() + " method1()");
			method2();
		}
	}
	
	/**
	 * 方法2,打印前获取 obj 锁
	 * 如果同一线程,锁不可重入的话,method2 需要等待 method1 释放 obj 锁
	 */
	public void method2() {
		synchronized (obj) {
			System.out.println(Thread.currentThread().getName() + " method2()");
		}
	}

	@Override
	public void run() {
		//线程启动 执行方法1
		method1();
	}
	
}

打印结果:

Thread-0 method1()
Thread-0 method2()

 

  • 测试 ReentrantLock 的可重入性
package constxiong.concurrency.a019;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 测试 ReentrantLock 的可重入性
 * @author ConstXiong
 * @date 2019-09-20 16:24:52
 */
public class TestLockReentrant {

	public static void main(String[] args) {
		new Thread(new LockReentrant()).start();
	}
	
}

class LockReentrant implements Runnable {

	private final Lock lock = new ReentrantLock();
	
	/**
	 * 方法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();
	}
	
}

打印结果:

Thread-0 method1()
Thread-0 method2()

 

测试不可重入锁

我在 JDK 中没找到可重入锁,所以考虑自己实现一下。两种方式:通过 synchronized wait notify 实现;通过 CAS + 自旋方式实现

1) synchronized wait notify 方式实现

package constxiong.concurrency.a019;


/**
 * 不可重入锁,通过 synchronized wait notify 实现
 * @author ConstXiong
 * @date 2019-09-20 16:53:34
 */
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();
	}

}

 

2) 通过 CAS + 自旋 方式实现

package constxiong.concurrency.a019;

import java.util.concurrent.atomic.AtomicReference;


/**
 * 不可重入锁,通过 CAS + 自旋 实现
 * @author ConstXiong
 * @date 2019-09-20 16:53:34
 */
public 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);
	}
}

 

测试类

package constxiong.concurrency.a019;

/**
 * 测试不可重入锁
 * @author ConstXiong
 * @date 2019-09-20 18:08:55
 */
public 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 的时候,导致了死锁,线程一直等待或者自旋下去。

 

参考:


 


 

所有资源资源汇总于公众号


 

 

posted @ 2019-11-30 20:45  ConstXiong  阅读(1888)  评论(0编辑  收藏  举报