1.ReentrantLock使用介绍
之前我们一直使用的Lock
实例都用的是ReentrantLock
,实际上,这是一种可重入锁。简单来说,就是对同一个线程可以进行多次的加锁操作。
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
还提供了一些工具方法,介绍如下。
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()
获取等待锁的线程数的预估值。
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
的源码来一探究竟吧。
public ReentrantLock() {
sync = new NonfairSync();
}
sync是什么?
代码语言:javascript复制abstract static class Sync extends AbstractQueuedSynchronizer {...}
原来是它自己的一个静态内部类,继承了AbstractQueuedSynchronizer
,我们后面将其简称为AQS
.AQS
里面的源码其实比较复杂,同时它也是我们的Lock
锁机制的核心之一。比如我们的lock()
操作其实就是调用的它的lock()
方法。
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