zoukankan      html  css  js  c++  java
  • ReentranLock浅析

    ReetrantLock属于可重入锁,Synchronized本身也是可重入锁。

    什么是可重入锁?简单的意思就是我锁了一下,还可以对同样这把锁再锁一下。

    比如说,有一个方法m1是sync的,在方法里面做了一个循环每次睡一秒,每隔一秒打印一下,接下来调用方法m2,m2也是一个sync的方法。

    分析一下,如果sync是不可重入锁的话,会是什么情况?第一个线程申请这把锁,锁的这个对象,然后这里如果是第二个线程来进行申请的话,他start不了,必须要等第一个线程结束了。因为这两个是不同的线程。两个线程之间肯定要争用的,可以在m1里面调用m2就可以,sync方法是可以调用sync方法的。

    synchronized void m1() {
          for (int i = 0; i < 10; i++) {
            try {
                    TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(i);
            if(i==2) 
                m2();
            }
        }
        synchronized void m2() {
            System.out.println("m2...");
        }
        public static void main(String[] args) {
            ReentrantLock02 lock02=new ReentrantLock02();
            //重入锁验证
            new Thread(lock02::m1).start();
        }

    从代码运行结果中可以看出,sync属于重入锁。ReentrantLock是可以替代synchronized的,将以上代码的synchronized位置换成lock.lock();加完锁之后,还要记得解锁。

    使用try...finally...,在finally里面一定要进行解锁处理lock.unlock()。

    ##但是既然ReentrantLock和Synchronized差不多的话,为什么要用它呢?

    既然ReentrantLock出现,那一定是有比Synchronized强大的地方,你可以使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行;synchronized如果搞不定的话,他就会阻塞了,但是用ReentrantLock你自己就可以决定要不要wait。

       //使用tryLock()代替lock.lock(),实现加锁的功能
       Lock lock=new ReentrantLock(); private void m() { boolean locked=false; try { locked=lock.tryLock(); if(locked) { for (int i = 0; i < 10; i++) { TimeUnit.SECONDS.sleep(1); System.out.println(i); } } } catch (Exception e) { e.printStackTrace(); }finally { if(locked) lock.unlock(); } }   public void m2(){
              boolean locked=false; try {
              
    //由于上一个线程执行时间超出5s,所以,得不到这把锁,会跳过继续执行后面的代码
              //如果将时间改成大于10s,则可以正常获取锁并输出结果    
                  
    locked=lock.tryLock(5, TimeUnit.SECONDS); if(locked) System.out.println("m2....."+locked); } catch (Exception e) { e.printStackTrace(); }finally {
                  
    if(locked)          
               lock.unlock(); } }
    public static void main(String[] args) { ReentrantLockTest01 test01=new ReentrantLockTest01();     new Thread(test01::m).start();   try {    TimeUnit.SECONDS.sleep(2);   } catch (Exception e) {    e.printStackTrace(); }   new Thread(test01::m2).start();
          }

    除了比synchronized多的这个功能之外,ReentrantLock()还可以用lock.lockInterruptibly()这个类,对interrupt()方法做出响应,表示可以被打断的加锁,如果以这种方式加锁的话,我们可以调用一个t2.interrupt();打断线程t2的等待。比如说一个线程执行过程中,内部进行了sleep操作,一直沉睡,而另外一个线程使用的是lock.lock(),他会一直在那儿等待,是无法打不断的。如果我们的2线程使用的是lock.lockInterruptibly()这个类可以被打断的,当你要想停止线程2就可以用Interrupt()。

    public static void main(String[] args) {
            Lock lock=new ReentrantLock();
            //线程1中拿到锁做了无限时间睡眠的操作,这样线程2会拿不到,一直处于等待。
            Thread t1=new Thread(()->{
                try {
                    lock.lock();
                    System.out.println("t1 start");
                    TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
                    System.err.println("t1 end");
                } catch (InterruptedException e) {
                    System.out.println("interrupted");
                }finally {
                    lock.unlock();
                }
            });
            t1.start();
            
            Thread t2=new Thread(()->{
                try {
                    //lock.lock();
                    /*
                     *     方法lockInterruptibly()可以对interrupt()方法做出响应,不再进行等待,释放资源
                     *     然后此处会捕获线程被打断的异常
                     */
                    lock.lockInterruptibly();
                    System.out.println("t2 start");
                    TimeUnit.SECONDS.sleep(5);
                    System.out.println("t2 end");
                } catch (InterruptedException e) {
                    System.out.println("t2 interrupted");
                }finally {
                    lock.unlock();
                }
            });
            t2.start();
            
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println(i);
                    TimeUnit.SECONDS.sleep(1);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
         //打断线程2的等待 t2.interrupt(); }

    ##ReentrantLock还可以表示公平锁,就是new的ReentrantLock()时,在括号中加入true。

    顾名思义,既然是公平锁,那一定是很公平的。取消了后来居上的情况,所有的线程都是遵循着先到者先行的规则。简单的说就是先等待锁的先执行(排队)。

    如果说这个锁是公平锁,来了一个线程想要执行的话,这个线程会先检查队列里有没有在它前面等待的,如果有的话,他就先进队列里等着别人先执行。

    ReentrantLock默认属于非公平锁。

        //公平锁,锁一定会分给下一排队等待的线程,不会出现抢锁插队现象
         //去除true或者改成false后,锁就变成了非公平锁,执行完全靠抢锁。
            private static ReentrantLock lock=new ReentrantLock(true);
            
            public void run(){
                for (int i = 0; i < 100; i++) {
                    lock.lock();
                    try {
                        System.err.println(Thread.currentThread().getName()+"得到锁");
                    } finally {
                        lock.unlock();
                    }
                }
            }
            public static void main(String[] args) {
                ReentrantLock04 lock04=new ReentrantLock04();
                Thread thread01=new Thread(lock04);
                Thread thread02=new Thread(lock04);
                thread01.start();
                thread02.start();
    }

    现在除了synchronized之外,大多数都是cas。但是谈到AQS的话,它的内部也是cas的,只不过做了一个比较隐蔽的锁升级。

  • 相关阅读:
    P7276-送给好友的礼物【dp】
    P4831-Scarlet loves WenHuaKe【组合数学】
    CF461D-Appleman and Complicated Task【并查集】
    P6499-[COCI2016-2017#2]Burza【状压dp】
    CF757F-Team Rocket Rises Again【最短路,DAG支配树】
    Loj#6053-简单的函数【Min25筛】
    P5325-[模板]Min_25筛
    2019.10.6 机房训练赛
    [CSP校内集训]v(记忆化搜索+map优化状压)
    [CSP校内集训]ac(树上启发式合并)
  • 原文地址:https://www.cnblogs.com/SpaceKiller/p/12626786.html
Copyright © 2011-2022 走看看