zoukankan      html  css  js  c++  java
  • ReentrantLock的实现语义与使用场景

    简介

    ReentrantLock(重入锁)就是支持可重进入的锁,它表示该锁能支持一个线程对资源的重复加锁。另外还支持获取锁的公平和非公平选择ReentrantLock的实现不仅可以替代隐式的synchronized关键字,而且还能够提供超过关键字本身的多种功能。

    公平与非公平

    这个概念是针对锁的获取的,在绝对时间上,先对锁进行获取的请求一定先满足,那么这个锁是公平的,反之就是不公平的。公平锁的获取就是等待时间最长的线程最先获取锁,也就是锁获取是顺序的。但是公平锁的机制往往效率不高。

    ReentrantLock的调用过程

    ReentrantLock把所有Lock接口的操作都委派到一个sync类上,该类继承了队列同步器:

    static abstract class Sync extends AbstractQueuedSynchronizer {
    final static class NonfairSync extends Sync
    final static class FairSync extends Sync

    1、sync有两个子类,分别实现的是公平锁和非公平锁,默认为非公平锁。

    2、由于该同步组件的实现使用的模板方法模式,我们看下ReentrantLock.lock()方法的调用过程(默认为非公平锁)

    非公平锁语义

    final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    if (compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                else if (current == getExclusiveOwnerThread()) {
                    int nextc = c + acquires;
                    if (nextc < 0) // overflow
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
            }

    1、首先判断锁当前的状态,如果锁空闲,则当前线程尝试获取。

    2、当前线程获取锁成功,则设置当前线程为锁的拥有者。

    3、如果锁状态为非空闲状态且当前线程为锁的拥有者,则直接将同步状态值进行增加并返回true,表示获取同步状态成功。

    这里就是支持锁重入的场景,跟synchronized类似:成功获取锁的线程再次获取锁,只是增加了同步状态值,与此对应的是,在释放锁的时候需要对应减少同步状态值,直到同步状态值为0的时候,才表示这把锁真正的被释放了。

    protected final boolean tryRelease(int releases) {
                int c = getState() - releases;
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                boolean free = false;
                if (c == 0) {
                    free = true;
                    setExclusiveOwnerThread(null);
                }
                setState(c);
                return free;
            }

    公平锁语义

     protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    if (isFirst(current) &&
                        compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                else if (current == getExclusiveOwnerThread()) {
                    int nextc = c + acquires;
                    if (nextc < 0)
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
            }

    这里与非公平锁大部分地方类似,只是线程在获取同步状态时,需要判断当前节点的前驱节点是否为首节点。公平锁总是按照队列的顺序来来获取锁的。

    ReentrantLock的使用场景

    1、发现该操作已经在执行中则不再执行

    a、用在定时任务时,如果任务执行时间可能超过下次计划执行时间,确保该任务只有一个正在执行,忽略重复触发。

    b、用在界面交互时点击执行较长时间请求操作时,防止多次点击导致后台重复执行。

    主要用于进行非重要任务时防止重复执行。

    private ReentrantLock lock = new ReentrantLock();
        
        public void test()
        {
            if(lock.tryLock())
            {
                try
                {
                   //dosomothing 
                }
               finally
               {
                   lock.unlock();
               }
            }
        }

    2、如果该操作已经在执行,则等待一个个执行(同步执行,与synchronized类似)

    3、如果该操作已经在执行,则尝试等待一段时间,超时则放弃执行

     if(lock.tryLock(5, TimeUnit.SECONDS))
            {
                try
                {
                   //dosomothing 
                }
               finally
               {
                   lock.unlock();
               }
            }

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

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

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

  • 相关阅读:
    [ Talk is Cheap Show me the CODE ] : jQuery Mobile工具栏
    Html 表格
    概率论机器学习的先验知识(上)
    ODPS 下一个map / reduce 准备
    Zygote过程【3】——SystemServer诞生
    Java获取的一天、本星期、这个月、本季度、一年等 开始和结束时间
    中国国家干部级别、职称级别、事业单位级别
    Multivariate Adaptive Regression Splines (MARSplines)
    在Visual Studio中开发Matlab mex文件,生成mexw64/mexw32
    Windows删除文件时出现,“正在准备 再循环”
  • 原文地址:https://www.cnblogs.com/dongguacai/p/5992838.html
Copyright © 2011-2022 走看看