zoukankan      html  css  js  c++  java
  • 关于ReentrantLock锁的一些理解

     简介

    ReentrantLock常常对比着synchronized来分析,我们先对比着来看然后再一点一点分析。

    (1)synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。

    (2)synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。

    (3)synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。

    场景1:如果发现该操作已经在执行中则不再执行(有状态执行)

    a、用在定时任务时,如果任务执行时间可能超过下次计划执行时间,确保该有状态任务只有一个正在执行,忽略重复触发。
    b、用在界面交互时点击执行较长时间请求操作时,防止多次点击导致后台重复执行(忽略重复触发)。

    以上两种情况多用于进行非重要任务防止重复执行,(如:清除无用临时文件,检查某些资源的可用性,数据备份操作等)

    这里写图片描述

     

    场景2:如果发现该操作已经在执行,等待一个一个执行(同步执行,类似synchronized)

    这种比较常见大家也都在用,主要是防止资源使用冲突,保证同一时间内只有一个操作可以使用该资源。
    但与synchronized的明显区别是性能优势(伴随jvm的优化这个差距在减小)。同时Lock有更灵活的锁定方式,公平锁与不公平锁

    ReentrantLock默认情况下为不公平锁

    不公平锁与公平锁的区别:
    公平情况下,操作会排一个队按顺序执行,来保证执行顺序。(会消耗更多的时间来排队)
    不公平情况下,是无序状态允许插队,jvm会自动计算如何处理更快速来调度插队。(如果不关心顺序,这个速度会更快)

    这里写图片描述

    场景3:如果发现该操作已经在执行,则尝试等待一段时间,等待超时则不执行(尝试等待执行)

    这种其实属于场景2的改进,等待获得锁的操作有一个时间的限制,如果超时则放弃执行。
    用来防止由于资源处理不当长时间占用导致死锁情况(大家都在等待资源,导致线程队列溢出)。

    这里写图片描述

    场景4:如果发现该操作已经在执行,等待执行。这时可中断正在进行的操作立刻释放锁继续下一操作

    synchronized与Lock在默认情况下是不会响应中断(interrupt)操作,会继续执行完。lockInterruptibly()提供了可中断锁来解决此问题。(场景2的另一种改进,没有超时,只能等待中断或执行完毕)

    这种情况主要用于取消某些操作对资源的占用。如:(取消正在同步运行的操作,来防止不正常操作长时间占用造成的阻塞)

    这里写图片描述

     

    下面是ReentrantLock的一个代码示例

    //: concurrency/AttemptLocking.java  
    // 以下是ReentrantLock中断机制的一个代码实现、如果换成synchronized就会出现死锁  
    import java.util.concurrent.*;  
    import java.util.concurrent.locks.*;  
    
    public class AttemptLocking {  
        private ReentrantLock lock = new ReentrantLock();  
    
        public void untimed() {  
            boolean captured = lock.tryLock();  
            try {  
                System.out.println("tryLock(): " + captured);  
            } finally {  
                if (captured)  
                    lock.unlock();  
            }  
        }  
    
        public void timed() {  
            boolean captured = false;  
            try {  
                captured = lock.tryLock(2, TimeUnit.SECONDS);  
            } catch (InterruptedException e) {  
                throw new RuntimeException(e);  
            }  
            try {  
                System.out.println("tryLock(2, TimeUnit.SECONDS): " + captured);  
            } finally {  
                if (captured)  
                    lock.unlock();  
            }  
        }  
    
        public static void main(String[] args) throws InterruptedException {  
            final AttemptLocking al = new AttemptLocking();  
            al.untimed(); // True -- 可以成功获得锁  
            al.timed(); // True --可以成功获得锁  
            //新创建一个线程获得锁并且不释放  
            new Thread() {  
                {  
                    setDaemon(true);  
                }  
    
                public void run() {  
                    al.lock.lock();  
                    System.out.println("acquired");  
                }  
            }.start();  
            Thread.sleep(100);// 保证新线程能够先执行  
            al.untimed(); // False -- 马上中断放弃  
            al.timed(); // False -- 等两秒超时后中断放弃  
        }  
    }  
  • 相关阅读:
    linux内核中GNU C和标准C的区别
    linux内核中GNU C和标准C的区别
    Getting start with dbus in systemd (02)
    Getting start with dbus in systemd (01)
    Getting start with dbus in systemd (03)
    物理内存相关的三个数据结构
    数据类型对应字节数(32位,64位 int 占字节数)
    Linux kernel 内存
    共模电感的原理以及使用情况
    [原创]DC-DC输出端加电压会烧毁
  • 原文地址:https://www.cnblogs.com/mr-wuxiansheng/p/12997033.html
Copyright © 2011-2022 走看看