参考答案
1. 可重入锁
指在同一个线程在外层方法获取锁的时候,进入内层方法会自动获取锁。
为了避免死锁的发生,JDK 中基本都是可重入锁。
测试下 synchronized 和 java.util.concurrent.lock.ReentrantLock 锁的可重入性:
synchronized 加锁,可重入性
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()
2. 测试 ReentrantLock 的可重入性
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()
3. 测试不可重入锁
由于在 JDK 中没找到不可重入锁,所以自己尝试实现。
两种方式:通过 synchronized wait notify 实现;通过 CAS + 自旋方式实现。
3.1 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(); } }
3.2 通过 CAS + 自旋方式实现
import java.util.concurrent.atomic.AtomicReference; 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); } }
测试类:
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 的时候,导致了死锁,线程一直等待或者自旋下去。
以上,是Java面试题【可重入锁与不可重入锁之间的区别与性能差异】的参考答案。
输出,是最好的学习方法。
欢迎在评论区留下你的问题、笔记或知识点补充~
—end—