Monitor模式是一种常见的并行开发机制, 一个Monitor实例可以被多个线程安全使用, 所有的monitor下面的方法在运行时是互斥的, 这种互斥机制机制可以用于一些特性, 例如让线程等待某种条件, 在等待时线程会将CPU时间交出去, 但是在条件满足时确保重新获得CPU时间. 在条件达成时, 你可以同时通知一个或多个线程. 这样做有以下的优点:

  1. 所有的同步代码都集中在一起, 用户不需要知道这是如何实现的
  2. 代码不依赖于线程数量, 线程数量只取决于业务需要
  3. 不需要对某个互斥对象做释放, 不存在忘记的风险

一个Monitor的结构是这样的

public class SimpleMonitor {
public method void testA(){
//Some code
} public method int testB(){
return 1;
}
}

使用Java代码不能直接创建一个Monitor, 要实现Monitor, 需要使用Lock和Condition类. 一般使用的Lock是ReentrantLock, 例如

public class SimpleMonitor {
private final Lock lock = new ReentrantLock(); public void testA() {
lock.lock(); try {
//Some code
} finally {
lock.unlock();
}
} public int testB() {
lock.lock(); try {
return 1;
} finally {
lock.unlock();
}
}
}

如果不需要判断条件, 那么用synchronized就可以了. 在需要判断条件的情况下, 使用Lock的newCondition()方法创建Condition, 可以通过Condition的await方法, 让当前线程wait, 放弃cpu时间. 然后用signal或者signalAll方法让线程重新获得CPU时间. signalAll方法会唤起所有wait在当前condition的线程. 下面是一个例子, 一个需要被多个线程使用的容量固定的buffer.

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class BoundedBuffer {
private final String[] buffer;
private final int capacity; private int front;
private int rear;
private int count; private final Lock lock = new ReentrantLock(); private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition(); public BoundedBuffer(int capacity) {
super(); this.capacity = capacity; buffer = new String[capacity];
} public void deposit(String data) throws InterruptedException {
lock.lock(); try {
while (count == capacity) {
notFull.await();
} buffer[rear] = data;
rear = (rear + 1) % capacity;
count++; notEmpty.signal();
} finally {
lock.unlock();
}
} public String fetch() throws InterruptedException {
lock.lock(); try {
while (count == 0) {
notEmpty.await();
} String result = buffer[front];
front = (front + 1) % capacity;
count--; notFull.signal(); return result;
} finally {
lock.unlock();
}
}
}

代码说明

  1. 这两个方法通过lock互斥
  2. 然后通过两个condition变量, 一个用于在buffer非空时等待, 一个用于buffer未满时等待
  3. 上面使用while循环将await包围, 这是为了防止在使用Signal&Condition时产生signal stealers问题.
  4. 以上方法可以安全地在多个线程中被调用

还有一个例子, 用于协调多个线程按固定顺序进行输出

public class TestSequentialThreads {
private final Lock lock = new ReentrantLock();
private final Condition[] conditions = {lock.newCondition(), lock.newCondition(), lock.newCondition()};
private int count = 0; public void action(int i) {
while (true) {
print(i + " wait lock");
lock.lock();
print(i + " has lock");
try {
while (count != i) {
print(i + " await");
conditions[i].await();
}
print("===== " + i + " =====");
Thread.sleep(500);
count = (count + 1) % 3;
int j = (i + 1) % 3;
print(i + " signal " + j);
conditions[j].signal();
} catch (InterruptedException e) {
print(i + " InterruptedException");
} finally {
print(i + " unlock");
lock.unlock();
}
}
} public static void main(String[] args) {
TestSequentialThreads ts = new TestSequentialThreads();
new Thread(()->ts.action(0)).start();
new Thread(()->ts.action(2)).start();
new Thread(()->ts.action(1)).start();
new Thread(()->ts.action(1)).start();
new Thread(()->ts.action(0)).start();
new Thread(()->ts.action(2)).start();
} public static void print(String str) {
System.out.println(str);
}
}

  

如果是使用wait()和notify()的话, 就要写成这样, 这种情况下, 运行时notify()随机通知的线程, 是有可能不满足而跳过的.

public class DemoThreadWait2 {
private Object obj = 0;
private int pos = 1; public void one(int i) {
synchronized (obj) {
if (pos == i) {
System.out.println("T-" + i);
pos = i % 3 + 1;
} else {
// System.out.println(".");
}
obj.notify();
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
DemoThreadWait2 demo = new DemoThreadWait2();
new Thread(()->{
while(true) {
demo.one(1);
}
}).start(); new Thread(()->{
while(true) {
demo.one(2);
}
}).start(); new Thread(()->{
while(true) {
demo.one(3);
}
}).start();
}
}

  

最新文章

  1. js的工作原理
  2. jacob下载问题, Office word 此文件正由另一应用程序或用户使用的解决方法
  3. BACKBONE源代码解析
  4. input type="submit" 和"button"有什么区别?
  5. Multiple dex files define
  6. 备忘====[HttpPost]和[AcceptVerbs(HttpVerbs.Post)]区别
  7. redis使用watch完成秒杀抢购功能:
  8. (转载)JDK中的设计模式
  9. jQuery 快速入门教程
  10. js添加删除元素
  11. python--简易购物车实现
  12. TsinsenA1221 大楼【矩阵快速幂】
  13. react native环境搭建与生命周期
  14. 了解一下Redis队列【缓兵之计-延时队列】
  15. MT【41】利用不等式妙消参数
  16. Spring框架整合Struts2框架的传统方法
  17. 评价指标1--F1值和MSE
  18. 详解Linux中CentOS6.8下解压安装mysql-5.7.14
  19. windows7 不能更新,提示:"WindowsUpdate_80240016" "WindowsUpdate_dt000",如何解决?
  20. 转!!Java虚拟机堆的内存分配和回收

热门文章

  1. appium自动化webview时遇到的chromedriver问题
  2. Jenkins+Docker+Git+Harbor流水线打包
  3. Java中的乐观锁
  4. MySQL/MariaDB数据库的函数
  5. danci6
  6. danci3
  7. linux中的alias命令详解
  8. django.db.models.fields.related_descriptors.RelatedObjectDoesNotExist: Course has no coursedetail.
  9. 用TortoiseSVN从github下载单个文件
  10. 纯js制作九宫格