3. ReentrantLock-可重入锁

相比较synchronized而言 ReentrantLock有以下特点:

  • 可中断
  • 可以设置超时时间
  • 可以设置为公平锁(防止线程饥饿)
  • 支持多个条件变量
  • 与synchronized 一样支持可重入

基本语法

首先需要创建一个ReentrantLock对象,以及获取锁,在try里面执行临界区代码,在finally里面释放锁,值得注意的是lock和unlock一定要成对出现

  ReentrantLock reentrantLock = new ReentrantLock();
        reentrantLock.lock();//获取锁
        try{
            //临界区
        }finally {
            reentrantLock.unlock();
        }

可重入

同synchronized一样,ReentrantLock是支持重入的如下所示

 static ReentrantLock reentrantLock = new ReentrantLock();
    public static void main(String[] args) {
        reentrantLock.lock();//获取锁
        try{
          log.debug("访问到Main的ReentrantLock的临界区");
          method();
        }finally {
            reentrantLock.unlock();
        }
    }
    public static void method(){
        reentrantLock.lock();//获取锁
        try{
            log.debug("访问到method的ReentrantLock的临界区");
        }finally {
            reentrantLock.unlock();
        }
    }

可打断

  • ReentrantLock提供了可打断的功能,是提供了lockInterruptibly()方法
  • lockInterruptibly()表示如果没有竞争就获取锁,如果有竞争就会进入阻塞队列中去,它与lock的区别是,lock进入阻塞队列是一直等待释放锁,lockInterruptibly()是可以被打断的
  • 如果通过interrupt()打断了,这时候线程是可以继续往下接着执行,但是是没有获取到锁的,如果还继续执行释放锁(unlock),就会报错,一般使用lockInterruptibly()的正确姿势是,如果被打断了在捕捉异常里直接return,如果没有被打断,在try{}finally{}里释放锁如下代码所示:
   static ReentrantLock reentrantLock = new ReentrantLock();
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    log.debug("尝试获取锁");
                    reentrantLock.lockInterruptibly();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    log.debug("被打断了");
                    return;
                }
                log.debug("打断了之后还能接着执行么");

                try{
                    log.debug("获取到锁");
                }finally {
                        reentrantLock.unlock();
                }
            }
        },"t1");
        reentrantLock.lock(); //主线程先获取锁
        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.interrupt();
    }

锁超时

  • ReentrantLock的tryLock方法 可以解决的死锁问题
  • 它首先尝试获取锁,如果没有没有获取到锁会返回false
  • 同时它可以使用tryLock(long n,TimeUnin)来表示如果获取锁的时候出现竞争,最多等待多久
  • 如果使用带参数的tryLock,一定要捕捉打断异常

公平锁

  • 为了解决锁饥饿的问题,可以使用ReentrantLock的公平锁机制来实现
  • 在构造方法中传入true,就可以实现公平锁机制
  • 尽量少使用公平锁,这会影响线程的并发度

条件变量

  • 在synchronized的多把锁中,可以对一个BigRoom,切成多个小Room,这样针对每一个小Room进行加锁,访问临界区的线程针对小Room的Monitor进入等待或者被唤醒,这个WaitSet就是条件变量
  • 到ReentrantLock中可能就没有那么复杂了,不会让使用者自己使用面向对象的方式创建一个个的Room,它有Condition来代替了自创建Room
  • 通过ReentrantLock.newCondition() 就能创建一个条件变量
  • Condition使用await&signal来通知该条件变量上的线程,与wait&nofity一样,使用此方法必须先获得锁

你可能感兴趣的:(3. ReentrantLock-可重入锁)