zoukankan      html  css  js  c++  java
  • java Concurrent包学习笔记(三):ReentrantLock

    一、可重入性的理解

    从名字上理解,ReenTrantLock的字面意思就是再进入的锁,其实synchronized关键字所使用的锁也是可重入的,两者关于这个的区别不大。两者都是同一个线程每进入一次,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。

    比如下面的代码片段:第一个lock没有unlock,就再次获取了lock

     private Lock lock = new ReentrantLock();
    lock.lock();
    执行一段代码
    lock.lock();
    再行执行一段代码
    lock.unlock();
    lock.unlock();

    lock()的流程图:

    unlock()的流程如下:

    二、主要方法

     jdk8中,ReentrantLock的方法:

    • lock():如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。如果当前线程已经保持该锁,则将保持计数加 1,并且该方法立即返回。如果该锁被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态
    • tryLock():马上返回,拿到lock就返回true,不然返回false。
    • tryLock((long timeout TimeUnit unit),马上返回,拿到lock就返回true,不然等到超时时间返回false。
    • lockInterruptibly(), 可中断加锁,即在锁获取过程中不处理中断状态,而是直接抛出中断异常,由上层调用者处理中断。ReentrantLock的中断和非中断加锁模式的区别在于:线程尝试获取锁操作失败后,在等待过程中,如果该线程被其他线程中断了,它是如何响应中断请求的。lock方法会忽略中断请求,继续获取锁直到成功;而lockInterruptibly则直接抛出中断异常来立即响应中断,由上层调用者处理中断。

    • unLock(): 释放锁
    • newCondition():返回用来与此 Lock 实例一起使用的 Condition 实例

    那么,为什么要分为这两种模式呢?这两种加锁方式分别适用于什么场合呢?根据它们的实现语义来理解,我认为lock()适用于锁获取操作不受中断影响的情况,此时可以忽略中断请求正常执行加锁操作,因为该操作仅仅记录了中断状态(通过Thread.currentThread().interrupt()操作,只是恢复了中断状态为true,并没有对中断进行响应)。如果要求被中断线程不能参与锁的竞争操作,则此时应该使用lockInterruptibly方法,一旦检测到中断请求,立即返回不再参与锁的竞争并且取消锁获取操作(即finally中的cancelAcquire操作)。

    三、Conditon对象

     Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition。

     Condition类能实现synchronized和wait、notify搭配的功能,另外比后者更灵活,Condition可以实现多路通知功能,也就是在一个Lock对象里可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择的进行线程通知,在调度线程上更加灵活

     synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在这个对象上。线程开始notifyAll时,需要通知所有的WAITING线程,没有选择权,会有相当大的效率问题。

    看下面的例子:

    Student类

    package reentrantlock.demo;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author Administrator
     * @date 2018/12/24
     */
    public class Student {
    
        private Lock lock = new ReentrantLock();
        private Condition conditionA = lock.newCondition();
        private Condition conditionB = lock.newCondition();
    
        public void awaitA() {
            try {
                lock.lock();
                System.out.println("学生A:等待吃饭");
                conditionA.await();
                System.out.println("学生A:开始吃饭");
                Thread.sleep(2000);
                System.out.println("学生A:吃完");
            }catch (InterruptedException e) {
            }finally {
                lock.unlock();
                System.out.println("学生A释放了锁======");
            }
        }
    
        public void awaitB() {
            try {
                lock.lock();
                System.out.println("学生B:等待吃饭");
                conditionB.await();
                System.out.println("学生B:开始吃饭");
                Thread.sleep(2000);
                System.out.println("学生B:吃完");
            }catch (InterruptedException e) {
            }finally {
                lock.unlock();
                System.out.println("学生B释放了锁======");
            }
        }
    
        public void signalAll_A() {
            try{
                lock.lock();
                System.out.println("唤醒学生A");
                conditionA.signalAll();
            } finally {
                lock.unlock();
            }
        }
    
        public void signalAll_B() {
            try{
                lock.lock();
                System.out.println("唤醒学生B");
                conditionA.signalAll();
            } finally {
                lock.unlock();
            }
        }
    
    }

    测试类

    package reentrantlock.demo;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * @author Administrator
     * @date 2018/12/24
     */
    
    
    
    public class ReentrantLockTest1 {
        class StudentThreadA implements Runnable{
            Student student;
            StudentThreadA(Student student){
                this.student = student;
            }
            public void run(){
                student.awaitA();
            }
        }
    
        class StudentThreadB implements Runnable{
            Student student;
            StudentThreadB(Student student){
                this.student = student;
            }
            public void run(){
                student.awaitB();
            }
        }
    
    
        public static void main(String[] args){
            ReentrantLockTest1 rl = new ReentrantLockTest1();
            Student student = new Student();
            ExecutorService executorService = Executors.newCachedThreadPool();
            executorService.submit(rl.new StudentThreadA(student));
            executorService.submit(rl.new StudentThreadB(student));
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            student.signalAll_A();
            executorService.shutdown();
    
        }
    }

    运行结果可知,只唤醒了学生A

    学生A:等待吃饭
    学生B:等待吃饭
    唤醒学生A
    学生A:开始吃饭
    学生A:吃完
    学生A释放了锁======

    四、Condition类和Object类

    • Condition类的awiat方法和Object类的wait方法等效
    • Condition类的signal方法和Object类的notify方法等效
    • Condition类的signalAll方法和Object类的notifyAll方法等效

    五、公平锁和非公平锁

    Lock lock=new ReentrantLock(true);//公平锁
    Lock lock=new ReentrantLock(false);//非公平锁

    六、synchronized和ReentrantLock的区别:
    1.等待可中断
    在持有锁的线程长时间不释放锁的时候,等待的线程可以选择放弃等待.   tryLock(long timeout, TimeUnit unit)
    2.公平锁
    按照申请锁的顺序来一次获得锁称为公平锁.synchronized的是非公平锁,ReentrantLock可以通过构造函数实现公平锁.    new RenentrantLock(boolean fair)
    3.绑定多个Condition
    通过多次newCondition可以获得多个Condition对象,可以简单的实现比较复杂的线程同步的功能.通过await(),signal(); 

  • 相关阅读:
    DataGridView使用SqlCommandBuilder批量更新数据
    【转】Python中中文处理的问题
    Logging模块的简单使用 Python
    Python 3 collections.defaultdict() 与 dict的使用和区别
    [转]关于Python的super用法研究
    Python 关于 name main的使用
    ClickOnce 我的大爱
    DataGridView控件显示行号的正确代码
    SQL存储过程和事务处理
    指针赋值的问题【转】
  • 原文地址:https://www.cnblogs.com/boshen-hzb/p/10157198.html
Copyright © 2011-2022 走看看