本篇文章已授权微信公众号 顾林海 独家发布
在 JDK1.5 中新增 ReentrantLock 类, 效果类似于使用 synchronized 关键字实现线程间同步互斥, 并且在扩展功能上也更加强大, 比如具有嗅探锁定, 多路分支通知等功能. 看下面 ReentrantLock 的实例代码:
- public class Task {
- private Lock mLock=new ReentrantLock();
- public void runTask(){
- mLock.lock();
- for(int i=0;i<3;i++){
- System.out.println(Thread.currentThread().getName()+"-->"+i);
- }
- mLock.unlock();
- }
- }
利用 ReentrantLock 对象的 lock() 方法获取锁, 通过 unlock() 方法释放锁.
- public class ThreadFirst extends Thread {
- private Task mTask;
- public ThreadFirst(Task task){
- this.mTask=task;
- }
- @Override
- public void run() {
- super.run();
- mTask.runTask();
- }
- }
- Client:
- public class Client {
- public static void main(String[] args) {
- Task task=new Task();
- ThreadFirst threadFirst;
- for (int i = 0; i <3; i++) {
- threadFirst=new ThreadFirst(task);
- threadFirst.start();
- }
- }
- }
打印:
- Thread-1-->0
- Thread-1-->1
- Thread-1-->2
- Thread-2-->0
- Thread-2-->1
- Thread-2-->2
- Thread-0-->0
- Thread-0-->1
- Thread-0-->2
从打印结果看, 打印是按分组来打印的, 当前线程打印完毕后释放锁, 其他线程才可以继续打印,
在前面文章中可以通过 synchronized 与 wait() 和 notify()/notifyAll() 方法相结合实现等待 / 通知模式, 利用 ReentrantLock 也可以实现同样的效果, 但需要借助于 Condition 对象. Condition 可以实现多路通知功能, 可以在一个 Lock 对象里面创建多个 Condition 实例, 线程对象可以注册在指定的 Condition 中, 从而可以有选择性进行线程通知.
代码示例:
- public class Task {
- private Lock mLock=new ReentrantLock();
- private Condition mCondition=mLock.newCondition();
- public void runTask(){
- try {
- mLock.lock();
- System.out.println("打印一些信息: A");
- mCondition.await();
- System.out.println("打印剩余信息: B");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- mLock.unlock();
- System.out.println("释放锁");
- }
- }
- }
线程类:
- public class ThreadFirst extends Thread {
- private Task mTask;
- public ThreadFirst(Task task){
- this.mTask=task;
- }
- @Override
- public void run() {
- super.run();
- mTask.runTask();
- }
- }
- Client:
- public class Client {
- public static void main(String[] args) {
- Task task=new Task();
- ThreadFirst threadFirst=new ThreadFirst(task);
- threadFirst.start();
- }
- }
打印:
打印一些信息: A
从打印结果看只打印了 A, 后面 B 没打印出来, 这是因为调用了 Condition 对象的 await() 方法, 使当前执行任务的线程进入等待 WAITING 状态.
补齐上面代码, 在 Task 类中新添加一个 notifyTask 方法:
- public void notifyTask(){
- try {
- mLock.lock();
- System.out.println("通知 task 继续执行");
- mCondition.signal();
- }finally {
- mLock.unlock();
- }
- }
Client 修改如下:
- public class Client {
- public static void main(String[] args) {
- Task task=new Task();
- ThreadFirst threadFirst=new ThreadFirst(task);
- threadFirst.start();
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- task.notifyTask();
- }
- }
打印:
打印一些信息: A
通知 task 继续执行
打印剩余信息: B
释放锁
到这里可以发现 Object 类的 wait() 和 wait(long) 方法相当于 Condition 类中的 await() 和 await(long) 方法. notify() 和 notifyAll 相当于 Condition 类中的 signal() 和 signalAll() 方法.
当然我们也可以创建多个 Condition 对象来实现通知部分线程, 代码如下:
- public class Task {
- private Lock mLock = new ReentrantLock();
- private Condition mConditionA = mLock.newCondition();
- private Condition mConditionB = mLock.newCondition();
- public void runTaskA() {
- try {
- mLock.lock();
- System.out.println("打印一些信息: A");
- mConditionA.await();
- System.out.println("打印剩余信息: A");
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- mLock.unlock();
- System.out.println("释放锁 A");
- }
- }
- public void runTaskB() {
- try {
- mLock.lock();
- System.out.println("打印一些信息: B");
- mConditionB.await();
- System.out.println("打印剩余信息: B");
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- mLock.unlock();
- System.out.println("释放锁 B");
- }
- }
- public void notifyTaskA() {
- try {
- mLock.lock();
- System.out.println("通知 A 继续执行");
- mConditionA.signal();
- } finally {
- mLock.unlock();
- }
- }
- public void notifyTaskB() {
- try {
- mLock.lock();
- System.out.println("通知 B 继续执行");
- mConditionB.signal();
- } finally {
- mLock.unlock();
- }
- }
- }
线程类 1:
- public class ThreadFirst extends Thread {
- private Task mTask;
- public ThreadFirst(Task task){
- this.mTask=task;
- }
- @Override
- public void run() {
- super.run();
- mTask.runTaskA();
- }
- }
线程类 2:
- public class ThreadSecond extends Thread {
- private Task mTask;
- public ThreadSecond(Task task){
- this.mTask=task;
- }
- @Override
- public void run() {
- super.run();
- mTask.runTaskB();
- }
- }
- Client:
- public class Client {
- public static void main(String[] args) {
- Task task=new Task();
- ThreadFirst threadFirst=new ThreadFirst(task);
- ThreadSecond threadSecond=new ThreadSecond(task);
- threadFirst.start();
- threadSecond.start();
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- task.notifyTaskA();
- }
- }
打印:
打印一些信息: A
打印一些信息: B
通知 A 继续执行
打印剩余信息: A
释放锁 A
从打印结果可以看出, 使用 ReentrantLock 对象可以唤醒指定种类的线程, 非常方便.
锁 Lock 分为 "公平锁" 和 "非公平锁", 公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的, 即先到先得的 FIFO 先进先出顺序, 而非公平锁就是一种获取锁的抢占机制, 是随机获得的, 和公平锁不一样的就是先来的不一定先得到锁, 这样的话可能造成某些线程一直拿不到锁.
来源: https://juejin.im/post/5c32c73851882526093f738f