前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Juc并发编程04——可重入锁、公平锁与非公平锁

Juc并发编程04——可重入锁、公平锁与非公平锁

作者头像
用户10127530
发布2022-10-26 17:59:06
1780
发布2022-10-26 17:59:06
举报
文章被收录于专栏:半旧的技术栈半旧的技术栈

1.ReentrantLock使用介绍

之前我们一直使用的Lock实例都用的是ReentrantLock,实际上,这是一种可重入锁。简单来说,就是对同一个线程可以进行多次的加锁操作。

代码语言:javascript
复制
public class Demo11 {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        lock.lock();
        lock.lock();
        new Thread(() -> {
            System.out.println("thread2 try to lock");
            lock.lock();
            System.out.println("thread2 lock successfully");
        }).start();
        lock.unlock();
        System.out.println("thread1 unlock one time");
        lock.unlock();
        System.out.println("thread2 unlock twice");
    }
}

其输出如下。

代码语言:javascript
复制
thread1 unlock one time
thread2 unlock twice
thread2 try to lock
thread2 lock successfully

也可能如下。其共同点是,只有线程1两层锁都被释放了线程2才能成功的获取到锁。

代码语言:javascript
复制
thread1 unlock one time
thread2 try to lock
thread2 unlock twice
thread2 lock successfully

ReentrantLock还提供了一些工具方法,介绍如下。

代码语言:javascript
复制
public class Demo12 {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        lock.lock();
        lock.lock();
        System.out.println("lock count:" + lock.getHoldCount() + ", isLock: " + lock.isLocked());
        lock.unlock();
        System.out.println("lock count:" + lock.getHoldCount() + ", isLock: " + lock.isLocked());
        lock.unlock();
        System.out.println("lock count:" + lock.getHoldCount() + ", isLock: " + lock.isLocked());
    }
}

如果一把锁被一个线程所持有,在其它线程获取锁时,是会进入等待队列的。可以使用getQueueLength()获取等待锁的线程数的预估值

代码语言:javascript
复制
public class Demo13 {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        lock.lock();
        Thread t1 = new Thread(lock::lock);
        Thread t2 = new Thread(lock::lock);
        t1.start();
        t2.start();
        Thread.sleep(1);
        System.out.println(lock.getQueueLength());
        System.out.println(lock.hasQueuedThread(t1));
        System.out.println(lock.hasQueuedThread(t2));
        System.out.println(lock.hasQueuedThread(Thread.currentThread()));
    }
}

其结果如下。

代码语言:javascript
复制
2
true
true
false

同样的,Condition类也可以做类似判断。输出结果依次是1,0.这种api多用才能记住,很枯燥。

代码语言:javascript
复制
public class Demo14 {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        new Thread(() -> {
            lock.lock();
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.unlock();
        }).start();
        TimeUnit.SECONDS.sleep(1);
        lock.lock();
        System.out.println(lock.getWaitQueueLength(condition));
        condition.signal();
        System.out.println(lock.getWaitQueueLength(condition));
        lock.unlock();
    }

2.公平锁与非公平锁

线程通过lock()来获取锁,如果获取不到会暂时进入等待队列中。那么,多个等待的线程获取锁的先后顺序是否与调用lock()的时间顺序是一致的呢?

读一读ReentrantLock的源码来一探究竟吧。

代码语言:javascript
复制
    public ReentrantLock() {
        sync = new NonfairSync();
    }

sync是什么?

代码语言:javascript
复制
abstract static class Sync extends AbstractQueuedSynchronizer {...}

原来是它自己的一个静态内部类,继承了AbstractQueuedSynchronizer,我们后面将其简称为AQS.AQS里面的源码其实比较复杂,同时它也是我们的Lock锁机制的核心之一。比如我们的lock()操作其实就是调用的它的lock()方法。

代码语言:javascript
复制
  public void lock() {
        sync.lock();
    }

后面我们会对AQS详细的进行介绍。这里我们再回过头看看ReentrantLock的构造方法。从名字上可以看出新建了一个非公平锁对象NonfairSync

  • 公平锁:获取锁的线程根据获取锁的顺序在队列中排队,先到先服务。
  • 非公平锁:多个线程在获取锁的时候,调用lock()时会直接尝试获取锁,如果获取不到再进入等待队列,如果获取到锁则直接拥有锁。

实际上它还有重载方法,可以指定使用公平锁还是非公平锁。

代码语言:javascript
复制
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
}

这里写个demo做简单的功能测试。先看看公平锁。

代码语言:javascript
复制
public class Demo15 {

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock(true);
        Runnable action = () -> {
            System.out.println("thread" + Thread.currentThread().getName() + "try to lock");
            lock.lock();
            System.out.println("thread" + Thread.currentThread().getName() + "lock successfully");
            lock.unlock();
        };

        for (int i = 0; i < 10; i++) {
            new Thread(action).start();
        }
    }
}

输出如下。

代码语言:javascript
复制
public class Demo15 {

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock(true);
        Runnable action = () -> {
            System.out.println("thread" + Thread.currentThread().getName() + "try to lock");
            lock.lock();
            System.out.println("thread" + Thread.currentThread().getName() + "lock successfully");
            lock.unlock();
        };

        for (int i = 0; i < 10; i++) {
            new Thread(action).start();
        }
    }
}

输出如下,细心的同学可能发现2先获取锁,但是1先获取锁,除此之外是公平的。可见公平锁不一定完全公平,后面我们将详细介绍这一点。

代码语言:javascript
复制
threadThread-0try to lock
threadThread-0 lock successfully
threadThread-1try to lock
threadThread-2try to lock
threadThread-3try to lock
threadThread-4try to lock
threadThread-2 lock successfully
threadThread-5try to lock
threadThread-6try to lock
threadThread-8try to lock
threadThread-7try to lock
threadThread-1 lock successfully
threadThread-3 lock successfully
threadThread-4 lock successfully
threadThread-5 lock successfully
threadThread-9try to lock
threadThread-6 lock successfully
threadThread-8 lock successfully
threadThread-7 lock successfully
threadThread-9 lock successfully

非公平锁

代码语言:javascript
复制
public class Demo15 {

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock(false);
        Runnable action = () -> {
            System.out.println("thread" + Thread.currentThread().getName() + "try to lock");
            lock.lock();
            System.out.println("thread" + Thread.currentThread().getName() + " lock successfully");
            lock.unlock();
        };

        for (int i = 0; i < 10; i++) {
            new Thread(action).start();
        }
    }
}

输出如下。

代码语言:javascript
复制
threadThread-1try to lock
threadThread-3try to lock
threadThread-2try to lock
threadThread-0try to lock
threadThread-1 lock successfully
threadThread-6try to lock
threadThread-6 lock successfully
threadThread-5try to lock
threadThread-5 lock successfully
threadThread-4try to lock
threadThread-4 lock successfully
threadThread-3 lock successfully
threadThread-7try to lock
threadThread-9try to lock
threadThread-8try to lock
threadThread-7 lock successfully
threadThread-2 lock successfully
threadThread-0 lock successfully
threadThread-9 lock successfully
threadThread-8 lock successfully
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-04-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.ReentrantLock使用介绍
  • 2.公平锁与非公平锁
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档