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

2022-10-26 17:59:06 浏览数 (1)

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

0 人点赞