ReentrantLock

  Java 除了使用关键字 Synchronized 外,还可以使用 ReentrantLock 实现独占锁的功能。而且 ReentrantLock 相比 Synchronized 而言功能更加丰富,使用起来更为了灵活,也更适用于复杂的并发场景。

 

1. 简介

  ReentrantLock 常常对比着 Synchronized 来分析

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

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

  3)Synchronized 不可响应中断,一个线程获取不到锁就一直等着,ReentrantLock 可以响应中断

 

  从上面看 ReentrantLock 比 Synchronized 没好太多,但是,Synchronized 所没有的一个最主要的就是 ReentrantLock 还可以实现公平锁机制。公平锁就是在锁上等待时间最长的线程获得锁的使用权。

 

2. 使用

  1)简单使用

  

public class MyReenterLock {

    public static final Lock lock = new ReentrantLock(true);

    public static void test(){
        try{
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " 获取了锁 ");
            TimeUnit.SECONDS.sleep(2);
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        new Thread(() -> test(), "线程A").start();
        new Thread(() -> test(), "线程B").start();
    }
}
View Code

  

  2)公平锁

  public static final Lock lock = new ReentrantLock(true);

 

  3)非公平锁

  public static final Lock lock = new ReentrantLock(false);

 

  4)响应中断

  响应中断就是一个线程获取不到锁,不会傻傻的等下去,ReentrantLock 会给予一个中断回应。

  

public class MyReentrantLock2 {
    static Lock lock1 = new ReentrantLock();
    static Lock lock2 = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new ThreadDemo(lock1, lock2));
        Thread t2 = new Thread(new ThreadDemo(lock2, lock1));
        t1.start();
        t2.start();
        t2.interrupt();
    }
}

class ThreadDemo implements Runnable{
    Lock firstLock;
    Lock secondLock;

    public ThreadDemo(Lock firstLock, Lock secondLock){
        this.firstLock = firstLock;
        this.secondLock = secondLock;
    }

    @Override
    public void run() {
        try{
            firstLock.lockInterruptibly();
            TimeUnit.SECONDS.sleep(1);
            secondLock.lockInterruptibly();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            firstLock.unlock();
            secondLock.unlock();
            System.out.println(Thread.currentThread().getName() + "  获取到资源,正常结束!");
        }
    }
}
View Code

  

  5)限时等待

  通过 tryLock 方法,可以选择传入时间参数,表示等待指定的时间,无参则表示立即返回锁申请的结果:true 表示获取锁成功,false 表示获取锁失败。

  if(!firstLock.tryLock()){TimeUnit.SECONDS.sleep(1);}

  我们也可以设置 tryLock 的超时等待时间 try(long timeout, TimeUnit unit),也就是说一个线程在指定的时间内没有获取到锁,就会返回 false,就可以做其他事情了。

 

 3. 应用场景

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

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

  2)用在界面交互时点击执行较长时间请求操作时,防止多次点击导致后台重复执行(忽略重复触发)

   这两种情况多用于进行非重要任务防止重复执行

  if(lock.tryLock()){ ..... lock.unlock();}

  

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

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

  这种情况主要用于对资源的争抢(如:文件操作,同步消息发送,有状态的操作等)

  lock.lock();  // 如果被其他资源锁定,会在此等待锁释放,达到暂停的效果

 

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

   // 如果已经被lock,则尝试等待 5s,看是否可以获得锁

  if(lock.tryLock(5, TimeUnit.SECONDS))

 

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

  Synchronized 与 Lock 在默认情况下不会响应中断操作,会继续执行完。lockInterruptibly() 提供可中断锁来解决此问题。

  这种情况主要用于取消某些操作对资源的占用。

 

 

https://baijiahao.baidu.com/s?id=1648624077736116382&wfr=spider&for=pc

https://blog.csdn.net/antony9118/article/details/52664125?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control

posted @ 2020-11-23 18:04  停不下的时光  阅读(153)  评论(0编辑  收藏  举报