• JUC-ReentrantLock


    可重入概念是不同的方法上加的是同一个锁,在一个方法中可以调用另一个方法,不会出现死锁的问题(意思就是我锁了一次之后还可以对同样这把锁再锁一次);

    一、回顾一下synchronized锁可重入性:

    记得在之前讲synchronized的时候讲过,synchronized就是可重入的。假设synchronized不可重入,字类和父类synchronized(this) ,this当然是同一把锁,在字类调用父类方法就会出现死锁。

    package com.dongl.juc.juc_008;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author D-L
     * @Classname T03_ReentrantLock01
     * @Version 1.0
     * @Description 可重入锁-synchronized
     * @Date 2020/7/22
     */
    public class T03_ReentrantLock01 {
    
        public synchronized void m1(){
            System.out.println("Method m1 is start ----------");
            for (int i = 0; i < 10; i++) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i);
                if(i == 5) m2();
            }
            System.out.println("Method m1 is end ----------");
        }
    
        private synchronized void m2() {
            System.out.println("Method m2 is running ----------");
        }
    
        public static void main(String[] args) {
          T03_ReentrantLock01 t = new T03_ReentrantLock01();
          new Thread(t::m1).start();
        }
    }

    二、ReentrantLock新类型的锁基于CAS)可重入性

    ReentrantLock可以替代synchronized,怎么替代呢?话不多说,直接看代码:

    package com.dongl.juc01.juc_001;
    
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author D-L
     * @Classname T01_ReentrantLock
     * @Version 1.0
     * @Description ReentrantLock代替synchronized 之前写synchronized地方换成lock.lock();
     *                lock加完锁之后,需要手动释放 synchronized加锁出现异常时 jvm会自动释放锁
     *                但是lock不会 如果出现异常没有手动释放就会出现死锁  try{}catch{}finally{lock.unlock} 手动释放
     * @Date 2020/7/22
     */
    public class T01_ReentrantLock {
        Lock lock = new ReentrantLock();
    
        /**方法m1**/
        public void m1(){
            System.out.println("Method m1 is start ----------");
            try {
                lock.lock();
                for (int i = 0; i < 10; i++) {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(i);
                    if (i == 5) m2();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //这里一定要手动解锁 synchronized出现异常时jvm会自动释放锁  但是lock必须手动释放
                lock.unlock();
            }
            System.out.println("Method m1 is end ----------");
        }
    
        /**方法m2**/
        public void m2(){
            try {
                lock.lock();
                System.out.println("Method m2 is running ----------");
            } finally {
                //手动释放锁
                lock.unlock();
            }
        }
        public static void main(String[] args) {
            T01_ReentrantLock t = new T01_ReentrantLock();
            new Thread(t::m1).start();
        }
    }

    有人会问既然reentrantlock和synchronized差不多问什么还要使用呢? 那当然时reentrantlock肯定有一些比synchronized强大的功能 ,锁获取与释放的可操作性,可中断的获取锁以及超时获取锁等synchronized关键字所不具备的同步特性。;

     

    三、下面就来看一看reentrantlock有什么特殊功能:

    • lock.tryLock(3,TimeUnit.SECONDS); 获取锁以及超时获取锁
    • lock.lockInterruptibly(); 中断

    1、lock.tryLock(3,TimeUnit.SECONDS);

    线程在调用lock方法来获得另一个线程所持有的锁的时候,很可能发生阻塞。应该更加谨慎地申请锁。tryLock 是防止自锁的一个重要方式 ,tryLock方法试图申请一个锁,在成功获得锁后返回true,否则,立即返回false,而且线程可以立即离开去做其他事。

    可以调用tryLock时,使用超时参数。
    lock方法不能被中断。如果一个线程在等待获得一个锁时被中断,中断线程在获得锁之前一直处于阻塞状态。如果出现死锁,那么,lock方法就无法终止。

    package com.dongl.juc01.juc_001;
    
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author D-L
     * @Classname T01_ReentrantLock
     * @Version 1.0
     * @Description ReentrantLock
     *
     * @Date 2020/7/22
     */
    public class T02_ReentrantLock {
        Lock lock = new ReentrantLock();
    
        /**方法m1**/
        public void m1(){
            System.out.println("Method m1 is start ----------");
            try {
                lock.lock();
                for (int i = 0; i <10; i++) {
                    System.out.println(i);
                }
                TimeUnit.SECONDS.sleep(5);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //这里一定要手动解锁 synchronized出现异常时jvm会自动释放锁  但是lock必须手动释放
                lock.unlock();
            }
            System.out.println("Method m1 is end ----------");
        }
    
        /**方法m2**/
        public void m2(){
            boolean locked = false;
    
            try {
                locked = lock.tryLock(10,TimeUnit.SECONDS);
                if(locked == true) System.out.println("Method m2 is running ----------");
                if(locked == false) System.out.println(Thread.currentThread().getName()+"获取到不到锁------------");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if(locked) lock.unlock();
            }
        }
    public static void main(String[] args) { T02_ReentrantLock t = new T02_ReentrantLock(); new Thread(t::m1 ,"Thread m1").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(t::m2,"Thread m2").start(); } }

     2、lock.lock(); lock.tryLock(5 , TimeUnit.SECONDS);lock.lockInterruptibly(); 响应interrupt()的区别:

    • lock 优先考虑获取锁,待获取锁成功后,才响应中断。
    • tryLock 尝试获得锁 在规定时间获取不到,就会抛出异常IllegalMonitorStateException。
    • lockInterruptibly 优先考虑响应中断,而不是响应锁的普通获取或重入获取。

    ReentrantLock.lockInterruptibly允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,如果未获得锁会抛出一个InterruptedException异常ReentrantLock.lock方法在线程未获得锁之前不允许Thread.interrupt中断,即使检测到Thread.isInterrupted,一样会继续尝试获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为interrupted状态,然后再中断线程。

    package com.dongl.juc01.juc_001;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author D-L
     * @Classname T03_ReentrantLock
     * @Version 1.0
     * @Description ReentrantLock
     *               lock.lock(); lock.tryLock(5 , TimeUnit.SECONDS);lock.lockInterruptibly();响应interrupt();的区别
     * @Date 2020/7/23
     */
    public class T03_ReentrantLock {
        final Lock lock = new ReentrantLock();
        public void m1() {
            System.out.println(Thread.currentThread().getName()+" start --------------------");
            try {
                lock.lock();
                TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);  //睡眠时间足够长
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName()+" interrupted.");
            } finally {
                lock.unlock();
            }
            System.out.println(Thread.currentThread().getName()+" end ----------------------");
        }
    
        public void m2() {
            System.out.println(Thread.currentThread().getName()+" start --------------------");
            try {
    //            lock.lock(); //优先获得锁 获得锁之后会响应中断
    //            lock.tryLock(5 , TimeUnit.SECONDS);  //尝试获得锁 在规定时间获取不到,就会抛出异常IllegalMonitorStateException
                lock.lockInterruptibly(); //可被终止 无论获没获取到锁 优先响应中断
    
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName()+" interrupted.");
            } finally {
                lock.unlock();
            }
            System.out.println(Thread.currentThread().getName()+" end ----------------------");
        }
        public static void main(String[] args) throws InterruptedException {
            T03_ReentrantLock t05 = new T03_ReentrantLock();
            Thread t1 = new Thread(t05::m1 ,"m1");
            Thread t2 = new Thread(t05::m2 ,"m2");
            //如果是m1先启动 m1获得锁之后要睡很久 m2很难在短时间里获得锁 不过lock.lockInterruptibly();优先考虑响应中断,
            // 而不是响应锁的普通获取或重入获取,所以此时抛出异常。
            t1.start();
            TimeUnit.SECONDS.sleep(1); //确保启动的优先级
            t2.start();
            //如果是m2先于m1获得锁 因为m1用的是lock.lock(); 优先获取锁成功后,才响应中断,如果获取不到就会一直等,
            // 直到获取锁才能响应中断 这里因为m2也睡了足够长时间 所以会一直等待 不会响应中断
    //        t2.start();
    //        TimeUnit.SECONDS.sleep(1);
    //        t1.start();
    
            TimeUnit.SECONDS.sleep(2);
            t2.interrupt();
    //        t1.interrupt();
        }
    }

    四、ReentrantLock自定义锁的公平性

    ReentrantLock可以自定义锁的公平性,意思就是可以通过传参(true  false)的方式来控制,ReentrantLock默认是非公平的,公平与非公平锁的队列都基于内部维护一个双向链表,首先上来先判断当前这把锁有多少线程持有(getState() 因为这里有锁的可重入的概念,锁重入一次state+=1)。如果state==0,说明此锁无线程持有,再看等待队列是否有等待线程,如果无等待线程,修改currentThread为独占线程,state+1;如果有等待线程,要看当前锁是否为公平锁,是公平锁,新建一个node放入队列队尾,等待cpu调度,非公平锁,上来直接尝试获取这把锁;

    公平锁:按照线程等待顺序获取锁,一般将获取锁失败的线程放入等待队列中,每次从FIFO队列的队头取出线程获取锁。这种方式会造成性能低下,大量的时间花费                在线程调度上。

    非公平锁:不管等待顺序,每个线程获取锁的概率都是相等的,优点是提高了响应速度,不用把大量时间花费在线程调度上,而是花费在执行代码上。

    package com.dongl.juc01.juc_001;
    
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author D-L
     * @Classname T03_ReentrantLock
     * @Version 1.0
     * @Description ReentrantLock  可指定公平与非公平锁  当设置位true时 此时为公平锁 也就是线程1和线程2会按顺序执行
     *                               当参数设置为false时 此时为非公平锁,线程会出现交叉执行
     * @Date 2020/7/23
     */
    public class T04_ReentrantLock extends Thread{
        //定义ReentrantLock的公平与非公平
    //    final ReentrantLock lock = new ReentrantLock(true);
        final ReentrantLock lock = new ReentrantLock(false);
    
    
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                lock.lock();
                try {
                    try {
                        TimeUnit.MILLISECONDS.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"  required the lock ----------" + i);
                } finally {
                    lock.unlock();
                }
            }
        }
    
        public static void main(String[] args){
            T04_ReentrantLock t04 = new T04_ReentrantLock();
            Thread t1 = new Thread(t04);
            Thread t2 = new Thread(t04);
            t1.start(); t2.start();
        }
    }
  • 相关阅读:
    SVN简介
    TFS简介
    UML简介
    C#++c1FlexGrid+帮助文档09
    vmware虚拟机 C硬盘空间 无损扩容 新测
    批处理命令中set定义的两种变量介绍 计算机基础知识
    ASP.NET获取网站根目录(路径)
    VMware(bridge、NAT、host-only、custom)含义
    spring3.0+Atomikos 构建jta的分布式事务
    在做了 BasePage 时: 只有在配置文件或 Page 指令中将 enableSessionState 设置为 true 时,才能使用会话状态。还请确保在应用程序配置的 / / 节中包括
  • 原文地址:https://www.cnblogs.com/dongl961230/p/13361536.html
Copyright © 2020-2023  润新知