日常记录——多线程与高并发—ReentrantLock概念、原理、使用、ReentrantLock和synchronized比较

    技术2022-07-11  77

    一、概念

    ReentrantLock是接口lock的实现类,实现为基于CAS+AQS(AbstractQueuedSynchronizer构建锁和同步容器的框架)的可重入锁,默认为非公平锁,可通过构造器定义为公平锁。

    二、原理

    ReentrantLock获取锁的原理:先通过CAS判断锁资源是否被占用。如果此时已经有线程占据了锁,那就加入AQS队列并且被挂起。当锁被释放之后,排在队列队首的线程会被唤醒,然后CAS再次尝试获取锁。在这个时候,如果: 非公平锁:当排在队列队首获取锁资源,如果同时还有另一个线程进来尝试获取,那么有可能会让这个线程抢先获取,因为这时锁是未被占用的; 公平锁:如果同时还有另一个线程进来尝试获取,当它发现自己不是在队首的话,就会排到队尾,由队首的线程获取到锁。

    三、使用

    1.可重入:同一线程,获取两次锁,释放两次锁。

    public class reentrantlock { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); new Thread(() -> { try { lock.lock(); lock.lock(); }catch (Exception e){ }finally { lock.unlock(); lock.unlock(); } }).start(); } }

    2.tryLock:尝试获取锁。获取失败返回fasle,执行逻辑。

    public class trylocks { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); new Thread(() -> { boolean flag = false; Thread.currentThread().setName("线程A"); try { System.out.println(Thread.currentThread().getName()+"开始执行"); flag = lock.tryLock(3, TimeUnit.SECONDS); if(flag){ Thread.currentThread().sleep(5000); System.out.println(Thread.currentThread().getName()+"获取到锁"); }else{ System.out.println(Thread.currentThread().getName()+"未获取到锁"); } }catch (Exception e){ e.printStackTrace(); }finally { if(flag){ lock.unlock(); } } }).start(); new Thread(() -> { boolean flag = false; Thread.currentThread().setName("线程B"); try { System.out.println(Thread.currentThread().getName()+"开始执行"); lock.tryLock(); flag = lock.tryLock(3, TimeUnit.SECONDS); if(flag){ Thread.currentThread().sleep(5000); System.out.println(Thread.currentThread().getName()+"获取到锁"); }else{ System.out.println(Thread.currentThread().getName()+"未获取到锁"); } }catch (Exception e){ e.printStackTrace(); }finally { if(flag) { lock.unlock(); } } }).start(); } }

    3.公平锁:在锁上等待时间最长的线程将获得锁的使用权。大部分情况下我们使用非公平锁,因为其性能比公平锁好很多,不需要cpu切换线程。但是公平锁能够避免线程饥饿。

    public class sameLock { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(true); for(int i =0;i<5;i++){ new Thread(() ->{ Thread.currentThread().setName("线程"+Math.random()); lock.lock(); System.out.println(Thread.currentThread().getName()+"获取到锁"); lock.unlock(); lock.lock(); System.out.println(Thread.currentThread().getName()+"获取到锁"); lock.unlock(); }).start(); } } }

    4.lockInterruptibly:用来接收线程被打断信号,接收到抛出异常,防止线程因为获取不到锁资源一直在阻塞状态。

    public class Interruptibly { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); Thread t1 = new Thread(() -> { System.out.println("t1开始执行"); try { lock.lock(); Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } System.out.println("t1执行结束"); }); Thread t2 = new Thread(() -> { System.out.println("t2开始执行"); try { lock.lockInterruptibly(); } catch (InterruptedException e) { System.out.println("t2被打断"); e.printStackTrace(); }finally { lock.unlock(); } System.out.println("t2执行结束"); }); long start = System.currentTimeMillis(); t1.start(); t2.start(); long end = System.currentTimeMillis(); //等待时间超过5s,打断t2线程。 while (end-start<5000){ end = System.currentTimeMillis(); } t2.interrupt(); } }

    四、ReentrantLock和synchronized比较

    1.synchronized是java内置的关键字,ReentrantLock是接口lock实现类。都可重入。 2.ReentrantLock可以尝试获取锁,根据结果执行逻辑,synchronized直接等待。 3.ReentrantLock需要手动释放锁,synchronized由JVM管理。 4.ReentrantLock可设定为公平锁。

    Processed: 0.014, SQL: 9