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

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

一个Monitor的结构是这样的

  1. public class SimpleMonitor {
  2. public method void testA(){
  3. //Some code
  4. }
  5.  
  6. public method int testB(){
  7. return 1;
  8. }
  9. }

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

  1. public class SimpleMonitor {
  2. private final Lock lock = new ReentrantLock();
  3.  
  4. public void testA() {
  5. lock.lock();
  6.  
  7. try {
  8. //Some code
  9. } finally {
  10. lock.unlock();
  11. }
  12. }
  13.  
  14. public int testB() {
  15. lock.lock();
  16.  
  17. try {
  18. return 1;
  19. } finally {
  20. lock.unlock();
  21. }
  22. }
  23. }

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

  1. import java.util.concurrent.locks.Condition;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4.  
  5. public class BoundedBuffer {
  6. private final String[] buffer;
  7. private final int capacity;
  8.  
  9. private int front;
  10. private int rear;
  11. private int count;
  12.  
  13. private final Lock lock = new ReentrantLock();
  14.  
  15. private final Condition notFull = lock.newCondition();
  16. private final Condition notEmpty = lock.newCondition();
  17.  
  18. public BoundedBuffer(int capacity) {
  19. super();
  20.  
  21. this.capacity = capacity;
  22.  
  23. buffer = new String[capacity];
  24. }
  25.  
  26. public void deposit(String data) throws InterruptedException {
  27. lock.lock();
  28.  
  29. try {
  30. while (count == capacity) {
  31. notFull.await();
  32. }
  33.  
  34. buffer[rear] = data;
  35. rear = (rear + 1) % capacity;
  36. count++;
  37.  
  38. notEmpty.signal();
  39. } finally {
  40. lock.unlock();
  41. }
  42. }
  43.  
  44. public String fetch() throws InterruptedException {
  45. lock.lock();
  46.  
  47. try {
  48. while (count == 0) {
  49. notEmpty.await();
  50. }
  51.  
  52. String result = buffer[front];
  53. front = (front + 1) % capacity;
  54. count--;
  55.  
  56. notFull.signal();
  57.  
  58. return result;
  59. } finally {
  60. lock.unlock();
  61. }
  62. }
  63. }

代码说明

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

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

  1. public class TestSequentialThreads {
  2. private final Lock lock = new ReentrantLock();
  3. private final Condition[] conditions = {lock.newCondition(), lock.newCondition(), lock.newCondition()};
  4. private int count = 0;
  5.  
  6. public void action(int i) {
  7. while (true) {
  8. print(i + " wait lock");
  9. lock.lock();
  10. print(i + " has lock");
  11. try {
  12. while (count != i) {
  13. print(i + " await");
  14. conditions[i].await();
  15. }
  16. print("===== " + i + " =====");
  17. Thread.sleep(500);
  18. count = (count + 1) % 3;
  19. int j = (i + 1) % 3;
  20. print(i + " signal " + j);
  21. conditions[j].signal();
  22. } catch (InterruptedException e) {
  23. print(i + " InterruptedException");
  24. } finally {
  25. print(i + " unlock");
  26. lock.unlock();
  27. }
  28. }
  29. }
  30.  
  31. public static void main(String[] args) {
  32. TestSequentialThreads ts = new TestSequentialThreads();
  33. new Thread(()->ts.action(0)).start();
  34. new Thread(()->ts.action(2)).start();
  35. new Thread(()->ts.action(1)).start();
  36. new Thread(()->ts.action(1)).start();
  37. new Thread(()->ts.action(0)).start();
  38. new Thread(()->ts.action(2)).start();
  39. }
  40.  
  41. public static void print(String str) {
  42. System.out.println(str);
  43. }
  44. }

  

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

  1. public class DemoThreadWait2 {
  2. private Object obj = 0;
  3. private int pos = 1;
  4.  
  5. public void one(int i) {
  6. synchronized (obj) {
  7. if (pos == i) {
  8. System.out.println("T-" + i);
  9. pos = i % 3 + 1;
  10. } else {
  11. // System.out.println(".");
  12. }
  13. obj.notify();
  14. try {
  15. obj.wait();
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. }
  21.  
  22. public static void main(String[] args) {
  23. DemoThreadWait2 demo = new DemoThreadWait2();
  24. new Thread(()->{
  25. while(true) {
  26. demo.one(1);
  27. }
  28. }).start();
  29.  
  30. new Thread(()->{
  31. while(true) {
  32. demo.one(2);
  33. }
  34. }).start();
  35.  
  36. new Thread(()->{
  37. while(true) {
  38. demo.one(3);
  39. }
  40. }).start();
  41. }
  42. }

  

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. C# EF框架 频繁连接性能损耗

    目的 测试EF框架在一次连接中多次保存和多次连接的耗时对比 测试环境 数据库SqlServer 2012 R2 EF框架6.2.0版本 数据库内容 ID UserName Password Creat ...

  2. day 02 作业 预科

    目录 作业 作业 ==1.什么是编程== 通过使用编程语言做一些事情,表达一些自己的想法. ==2.简述计算机五大组成.== 计算机由cpu ,运算器 ,控制器,存储器(外存,内存) 输入设备 输出设 ...

  3. CentOS 使用163yum源

    下载163源 # wget http://mirrors.163.com/.help/CentOS7-Base-163.repo 如果报以下错误 -bash: wget: command not fo ...

  4. JavaScript: 详解正则表达式之三

    在上两篇文章中博主介绍了JavaScript中的正则常用方法和正则修饰符,今天准备聊一聊元字符和高级匹配的相关内容. 首先说说元字符,想必大家也都比较熟悉了,JS中的元字符有以下几种: / \ | . ...

  5. sklearn.feature_extraction.text 的TfidfVectorizer函数

    TfidfVectorizer函数主要用于,将文档(句子)等通过 tf-idf值来进行表示,也就是用一个tf-idf值的矩阵来表示文档(句子也可). from sklearn.feature_extr ...

  6. AXURE RP EXTENSION For Chrome----解决办法

    出现这个问题是因为chrome://extensions/中没有安装扩展程序 解决办法: 步骤一:情景再现,打开某个html会出现如下页面,不停地提示你安装插件 步骤二:如果点击安装扩展程序,能够成功 ...

  7. 使用Arduino和SD卡模块记录数据并导出到Excel

    在本篇文章中,我们将学习如何基于Arduino开发板使用一个SD卡模块.同时结合DS3231实时时钟模块,我们将制作一个数据记录仪的示例,在示例中,我们将温度传感器的数据存储到SD卡中,并将这些数据导 ...

  8. xpath+多进程爬取全书网纯爱耽美类别的所有小说。

    # 需要的库 import requests from lxml import etree from multiprocessing import Pool import os # 请求头 heade ...

  9. python笔记35-装饰器

    前言 python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象. 很多python初学者学到面向对象类和方法是一道大坎,那么p ...

  10. spring的面试题

    什么是spring? spring是一个开源框架,为简化企业级应用开发而生.Spring可以是使简单的javaBean实现以前只有EJB才能实现的功能.Spring是一个IOC和AOP容器框架. Sp ...