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的,只不过做了一个比较隐蔽的锁升级。

posted @ 2020-04-03 15:32  我是白小白  阅读(275)  评论(0)    收藏  举报