并发编程-ReentrantLock类走读

背景

并发编程中需要使用锁在进行线程间同步和禁止同时访问共有资源的。下面走读下juc包中的ReentrantLock

血缘关系

ReentrantLock血缘

  • Lock接口类,属于jdk中juc包,锁的基类(不包括synchronize关键字)
  • ReentrantLock,可重入锁,实现了Lock

在这里插入图片描述

Lock的其他派生类血缘

在这里插入图片描述
可以看到,在juc的包中,除了读写锁,就没其他的锁实现Lock了,可见原生的锁种类并不繁多(自旋锁、乐观悲观锁等都是一种概念和方法,在java中并没有特殊的实现类)。

源码走读

Lock接口类

在这里插入图片描述

  • lock,获取锁资源(不可中断)
  • lockInterruptibly,获取锁资源,但是可以在等待中被中断
  • tryLock,尝试获取锁资源,如果获取不到,则返回false
  • unlock,释放锁
  • newCondition,创建同步实例

ReentrantLock类

在这里插入图片描述

  • getHoldCount,查看同时锁住的线程个数
  • isHeldByCurrentThread,是否被当前线程获取锁
  • isLocked,是否被获取
  • isFair,是否是公平锁(构造函数中可以指定)
  • hasQueuedThreads,是否有线程在排队获取锁
  • getQueueLength,获取排队线程数量
  • hasWaiters,指定condition是否有await(需要在lock内使用)
  • getWaitQueueLength,获取await的数量(需要在lock内使用)

使用ReentrantLock需要注意一下几点(之后的实战中也将体现):

  • 线程在lock后的操作时,其他线程不能再lock,这是锁的最基础使用了
  • 使用await和signal需要在lock之后,unlock之前
  • 线程在lock之后,又进入await,那么其他线程可以继续lock,之后的实战将有所体现

实战

isxxx方法实战

public class LockDemo {
    private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
    	// false,是否是公平锁
        System.out.println(lock.isFair());
        lock.lock();
        // true,是否已经上锁
        System.out.println(lock.isLocked());
        // true,是否被当前线程上锁
        System.out.println(lock.isHeldByCurrentThread());
        lock.unlock();
    }
}

在这里插入图片描述

holdxxx方法实战

public class LockDemo {
    private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
    	// false
        System.out.println(lock.isHeldByCurrentThread());
        lock.lock();
        // 1,重入次数
        System.out.println(lock.getHoldCount());
        // true
        System.out.println(lock.isHeldByCurrentThread());
        lock.unlock();

        lock.lock();
        lock.lock();
        // 2,重入次数
        System.out.println(lock.getHoldCount());
        lock.unlock();
        lock.unlock();
    }
}

queuedxxx方法实战

public class LockDemo {
    private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        createThread();
        createThread();
        createThread();
        // 为了能让线程启动完毕,进行10ms的睡眠
        Thread.sleep(10);
        System.out.println(lock.hasQueuedThreads());
        System.out.println(lock.getQueueLength());
    }

    public static void createThread() {
        new Thread(() -> {
            try {
                lock.lock();
                System.out.println("上锁");
                while (true);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
                System.out.println("解锁");
            }
        }).start();
    }
}

在这里插入图片描述
结果分析:

  • 创建了三个线程,第一个线程一直在跑while循环,另外两个线程则阻塞在lock.lock()的位置(因为第一个线程一直占用cpu,所以另外两个线程需要等待锁的释放)。
  • hasQueuedThreads,返回结果true,因为后面两个线程在等待
  • getQueueLength,返回结果2,因为一共2个线程在等待。

awaitxxx方法实战

public class LockDemo {
    private static ReentrantLock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();
    public static void main(String[] args) throws InterruptedException {
        createThread();
        createThread();
        createThread();
        Thread.sleep(10);
        System.out.println(lock.hasQueuedThreads());
        System.out.println(lock.getQueueLength());

        lock.lock();
        System.out.println(lock.hasWaiters(condition));
        System.out.println(lock.getWaitQueueLength(condition));
        condition.signalAll();
        lock.unlock();
    }

    public static void createThread() {
        new Thread(() -> {
            try {
                lock.lock();
                System.out.println("上锁" + Thread.currentThread().getName());
                condition.await();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
                System.out.println("解锁" + Thread.currentThread().getName());
            }
        }).start();
    }
}

在这里插入图片描述
结果分析:

  • 开始创建三个线程,但是都阻塞在await上(注意:lock之后进行await,不再占用cpu实现片,其他线程可以再调用lock
  • 没有线程在排队,而是都进入了await的状态
  • hasWaiters,返回结果true
  • getWaitQueueLength,返回结果3,说明先创建的三个线程都在await
  • 调用condition.signalAll(),对三个线程进行激活

点评

  • lock可以new多个Condition,进行灵活的控制,相比Object的wait+notify要灵活、安全。
  • 据说在synchronized优化后,同ReentrantLock的效率一样,特意写代码对比了一下,发现ReentrantLock的效率高一些,但是优势不明显
public class LockDemo {
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("synchronized lock");
        for (int i = 0; i < 20000000; i++) {
            testSynchronizedLock();
        }
        stopWatch.stop();
        stopWatch.start("reentrant lock");
        for (int i = 0; i < 20000000; i++) {
            testReentrantLock();
        }
        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
    }

    public static synchronized void testSynchronizedLock() {

    }

    public static void testReentrantLock() {
        lock.lock();
        lock.unlock();
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值