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();
}
}

  

Java线程同步的Monitor机制(Lock配合Condition)的更多相关文章

  1. (删)Java线程同步实现二:Lock锁和Condition

    在上篇文章(3.Java多线程总结系列:Java的线程同步实现)中,我们介绍了用synchronized关键字实现线程同步.但在Java中还有一种方式可以实现线程同步,那就是Lock锁. 一.同步锁 ...

  2. 转:C# 线程同步技术 Monitor 和Lock

    原文地址:http://www.cnblogs.com/lxblog/archive/2013/03/07/2947182.html 今天我们总结一下 C#线程同步 中的 Monitor 类 和 Lo ...

  3. Java线程同步_1

    Java线程同步_1 synchronized 该同步机制的的核心是同步监视器,任何对象都可以作为同步监视器,代码执行结束,或者程序调用了同步监视器的wait方法会导致释放同步监视器 synchron ...

  4. Java线程同步之一--AQS

    Java线程同步之一--AQS 线程同步是指两个并发执行的线程在同一时间不同时执行某一部分的程序.同步问题在生活中也很常见,就比如在麦当劳点餐,假设只有一个服务员能够提供点餐服务.每个服务员在同一时刻 ...

  5. JAVA - 线程同步和线程调度的相关方法

    JAVA - 线程同步和线程调度的相关方法 wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁:wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等 ...

  6. Java线程同步的四种方式详解(建议收藏)

    ​ Java线程同步属于Java多线程与并发编程的核心点,需要重点掌握,下面我就来详解Java线程同步的4种主要的实现方式@mikechen 目录 什么是线程同步 线程同步的几种方式 1.使用sync ...

  7. java 线程同步 原理 sleep和wait区别

    java线程同步的原理java会为每个Object对象分配一个monitor, 当某个对象(实例)的同步方法(synchronized methods)被多个线程调用时,该对象的monitor将负责处 ...

  8. java线程 同步临界区:thinking in java4 21.3.5

    java线程 同步临界区:thinking in java4 21.3.5 thinking in java 4免费下载:http://download.csdn.net/detail/liangru ...

  9. 多线程状态与优先级、线程同步与Monitor类、死锁

    一.线程状态 二.线程优先级 三.初步尝试多线程 class Program { static void Main(string[] args) { while (true) { MessagePri ...

随机推荐

  1. CentOS 7 使用 firewalld 打开关闭防火墙与端口

    1.firewalld的基本使用启动: systemctl start firewalld关闭: systemctl stop firewalld查看状态: systemctl status fire ...

  2. MySQL DDL--MySQL 5.7版本Online DDL操作

    主键索引维护 1.新增主键索引 ## 可以使用ALGORITHM=INPLACE+LOCK=NONE方式,操作期间允许读写. ALTER TABLE tb001 ADD PRIMARY KEY (ID ...

  3. C++ STL hash表用法

    C++ STL unordered_map用法 在C++11中,unordered_map作为一种关联容器,替代了hash_map,unordered_map的底层实现是hash表,所以被称为无序关联 ...

  4. Python并发编程-GIL全局解释器锁

    Python并发编程-GIL全局解释器锁 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.GIL全局解释器锁概述 CPython 在解释器进程级别有一把锁,叫做GIL,即全局解释 ...

  5. django项目基于钩子验证的注册功能

    前端html <div class="agile-row"> <h3>注册</h3> {# 注册的开始#} <div class=&quo ...

  6. CentOS7安装Supervisor3.1.4

    supervisord 负责管理进程的server端,配置文件是/etc/supervisor/supervisord.conf supervisorctl client端的命令行工具,管理子进程,配 ...

  7. python2和python3切换

    (1)需要将python2和python3的环境变量设置好 (2)重命名主程序 然后我们分别把两个版本的 Python 主程序 exe 改下名,3.6 版本的改名为 python3.exe,2.7 版 ...

  8. Python中str()与repr()函数的区别——repr() 的输出追求明确性,除了对象内容,还需要展示出对象的数据类型信息,适合开发和调试阶段使用

    Python中str()与repr()函数的区别 from:https://www.jianshu.com/p/2a41315ca47e 在 Python 中要将某一类型的变量或者常量转换为字符串对象 ...

  9. Kotlin协程重要概念详解【纯理论】

    在之前对Kotlin的反射进行了详细的学习,接下来进入一个全新的篇章,就是关于Koltin的协程[coroutine],在正式撸码之前先对它有一个全面理论化的了解: 协程的定义: 协和通过将复杂性放入 ...

  10. selenium中的等待方法及区别

    等待是为了使脚本执行更加稳定 常用的休眠方式: 1.time模块的sleep方法 :引入from time import sleep 2.implicitly_wait():设置webdriver等待 ...