zoukankan      html  css  js  c++  java
  • 可重入锁与不可重入锁之间的区别与性能差异?

    可重入锁

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

    为了避免死锁的发生,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 的时候,导致了死锁,线程一直等待或者自旋下去。

    参考:


     


     

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


     

  • 相关阅读:
    Ruby on Rails中的Rake教程(Rake如何把我灌醉!)
    十一、Spring Boot 集成Shiro和CAS
    Spring Boot 快速入门
    一位创业者的血泪史----献给85前创业者的反思
    罗振宇送给新员工的四句话
    Axure 入门
    XMind常用快捷方式汇总
    CAS 单点登陆
    mysql互换表中两列数据
    mysql默认安装目录说明
  • 原文地址:https://www.cnblogs.com/ConstXiong/p/11963881.html
Copyright © 2011-2022 走看看