解析JVM线程同步机制
http://blog.csdn.net/thl789/article/details/566494
对多线程的支持一般是在OS级的,而Java将其做在了语言级别,这其中最吸引人的莫过于Java对线程同步(互斥与协作)的支持。本文分析了JVM(Java Virtual Machine)内部实现的监视器同步机制,并结合经典的生产者消费者同步问题,阐述Java语言级别上对此机制的支持。
关键词:同步,互斥,协作,监视器,锁
Keyword: Synchronization, Mutual Exclusion (Mutex), Cooperation, Monitor, Lock
目 录
知识准备
阅读本文前,你应该具有操作系统的基本知识,知道Java的基本运行模式,最好还有过多线程的编程经验。这些知识准备,可以阅读本文后面所附的参考资料。
一、线程同步:互斥和协作
二、解决同步的方案
Java利用JVM对线程执行期的完全控制,实现了监视器(Monitor)机制的线程同步,下面章节先介绍监视器机制,然后分析监视器机制在JVM中的实现。
三、线程同步模型——监视器(Monitor)机制
监视器支持上文所述的两种线程同步:互斥与协作,而JVM通过对象锁实现了监视器机制。本节就来阐述监视器模型,并分析它如何实现线程的互斥与协作。
3.1 监视器模型
图一是监视器模型,监视器包括了三个部分,入口区、拥有区和等待区,入口区和等待区内可能有多个线程,但是任何时刻最多只有一个线程拥有该监视器。
监视器实现的是对临界区的管理,对临界区调度原则有16字要求——无空等待,有空让进,择一而入,算法可行。展开来说就是:
3.2 监视器实现互斥
3.3 监视器实现协作
上面小节主要说明了生产者和消费者对缓冲区的互斥访问关系,但是没有详细谈到“wait & Release the Monitor”这个活动,这个主要牵涉了监视器实现的线程间的协作关系。
生产者和消费者通过监视器协作完成生产者消费者问题的活动图如图三所示。
四、JVM线程同步的实现
JVM通过对象锁实现监视器的模型的线程同步机制。其实现是通过在JVM内部为每个对象和类都关联一个锁;语言层次上用同步方法或同步语句标识临界区,每个对象都实现等待/通知方法等方式来通过实现线程同步的。
4.1 JVM线程模型
4.2 对象锁
目前已经讨论了JVM如何实现线程同步的,但是还不知道如何进入/退出临界区,以及如何通知等待的线程实现线程协作的,这些都在Java语言上给予了支持,本文在下面章节具体介绍。
4.3 Java语言对线程同步的支持
Java语言上对线程同步的支持主要有对临界区的标识,和线程协作的支持。
4.3.1 同步方法与同步语句
4.3.2 协作——wait & notify/notifyAll
wait & notify/notifyAll的原型声明如下:
public final void wait() throws InterruptedException
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException
public final native void notify();
public final native void notifyAll();
等待某个对象锁的时候,可以指定等待的时间,超时的话,自动退出等待。
当明确知道等待区内只有一个等待线程的时候,才应该使用notify,否则就应该使用notifyAll,让JVM采用相应的调度策略来决定选择哪个等待该对象锁的线程被唤起。这样就可由JVM来保证避免某个线程无限制等待的饥饿现象,而不需要用户来关注。
五、利用JVM线程同步解决生产者消费者问题
5.1 生产者消费者问题相关类层次
5.2 生产者线程——threadSynch.ProducerConsumer.Producer
public void run() {
super.run();
while (true) {
execute();
}
}
execute()是生产者的循环执行体,实现的是图三的生产者的活动,代码如下:
private void execute() {
generateNewProduct();
System.out.println("[Producer] produced a new product: " + product);
synchronized (buffer) {
System.out.println("[Producer] Owned the buffer's monitor!");
while (!buffer.isNull()) {
System.out.println("[Producer] release the monitor and wait!");
try {
buffer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("[Producer] re-owned the monitor!");
}
System.out.println("[Producer] put the product (" + product
+ ") into the buffer!");
buffer.setProduct(product);
System.out.println("[Producer] notify other threads waiting on the monitor!");
buffer.notifyAll();
System.out.println("[Producer] to release the monitor!");
}
}
generateNewProduct()利用java.util.Random产生一个随机数代码生产者生产的产品。
private void generateNewProduct() {
int prd = product;
while ((prd = (new Random()).nextInt(100)) == product)
;
product = prd;
}
5.3 消费者线程——threadSynch.ProducerConsumer.Consumer
private void execute() {
synchronized (buff) {
System.out.println("[Consumer] Owned the monitor!");
while (buff.isNull()) {
System.out.println("[Consumer] release the monitor and wait!");
try {
buff.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("[Consumer] re-owned the monitor!");
}
product = buff.getProduct();
System.out.println("[Consumer] get a product (" + product
+ ") from the buffer!");
System.out.println("[Consumer] notify others waiting on the monitor!");
buff.notifyAll();
System.out.println("[Consumer] to release the monitor!");
}
System.out.println("[Consumer] consume the product: " + product);
}
5.4 主程序——threadSynch.ProducerConsumer. ProducerConsumer
主程序ProducerConsumer创建Buffer以及生产者和消费者线程的实例,并启动生产者和消费者线程。
package threadSynch.ProducerConsumer;
public class ProducerConsumer {
private static Producer producer;
private static Consumer consumer;
private static Buffer buffer;
public static void main(String[] args) {
buffer = new Buffer(0);
producer = new Producer(buffer);
consumer = new Consumer(buffer);
consumer.start();
producer.start();
}
}
5.5 程序运行时的一个快照
下面是上述程序运行时的一个快照,并分别以绿色和蓝色标识生产者和消费者的输出:
[Consumer] Owned the monitor! (01)
[Consumer] release the monitor and wait! (02)
[Producer] produced a new product: 43 (03)
[Producer] Owned the buffer's monitor! (04)
[Producer] put the product (43) into the buffer! (05)
[Producer] notify other threads waiting on the monitor! (06)
[Producer] to release the monitor! (07)
[Consumer] re-owned the monitor! (08)
[Consumer] get a product (43) from the buffer! (09)
[Consumer] notify others waiting on the monitor! (10)
[Consumer] to release the monitor! (11)
[Consumer] consume the product: 43 (12)
[Consumer] Owned the monitor! (13)
[Producer] produced a new product: 70 (14)
[Consumer] release the monitor and wait! (15)
[Producer] Owned the buffer's monitor! (16)
[Producer] put the product (70) into the buffer! (17)
[Producer] notify other threads waiting on the monitor! (18)
[Producer] to release the monitor! (19)
[Producer] produced a new product: 43 (20)
[Producer] Owned the buffer's monitor! (21)
[Producer] release the monitor and wait! (22)
[Consumer] re-owned the monitor! (23)
[Consumer] get a product (70) from the buffer! (24)
[Consumer] notify others waiting on the monitor! (25)
[Consumer] to release the monitor! (26)
[Consumer] consume the product: 70 (27)
[Consumer] Owned the monitor! (28)
[Consumer] release the monitor and wait! (29)
[Producer] re-owned the monitor! (30)
[Producer] put the product (43) into the buffer! (31)
[Producer] notify other threads waiting on the monitor! (32)
[Producer] to release the monitor! (33)
[Producer] produced a new product: 34 (34)
[Producer] Owned the buffer's monitor! (35)
[Producer] release the monitor and wait! (36)
[Consumer] re-owned the monitor! (37)
[Consumer] ―――――― (38)
[Producer] ―――――― (39)
生产者消费者相关联的对象的简写表示:Per-生产者;Cer-消费者;B-缓冲区;M-缓冲区相关联的监视器;Px(x = 1, 2, …)-产品。结合图三的生产者和消费者的活动图,可以解释上面的输出快照。
- 消费者Cer首先运行,获得监视器M,因缓冲区B内当前还没有产品P,所以Cer释放M并等待[Line 1, 2]。
- 生产者Per生产了一个产品P1,在获得M之后,放置P1到B并通知Cer,释放M[Line 3-7]。
- Cer重新获得M之后,从B中得到P1并清空B,通知其它线程当前B可用,释放M,消费产品P1(因为P1已经被Cer获得,所以消费P1不需要在临界区内完成),重新获得M(线程调度的作用)[Line 8-13]。
- Per生产一个新产品P2(虽然Per当前未获得M,但生产产品是不需要在临界区的,当JVM线程调度运行Per的时候,它仍然可以生产产品)[Line 14]。
- P2并未被放到B,所以Cer释放M并等待[Line 15]。
- Per现在可获得M[16],并放置P2到B[17],然后唤醒等待M的线程[18]并释放M[19];当前Cer并未获得调度运行,所以Per继续生产P3[20];Per获得M并试图放置P3到B[21],但此时Cer还未取走P2,所以Per释放M并等待[22];
- Cer…[23, …]
- Per…[line…]
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
六、总结
关于其它同步方法,Doug Lea实现了一个Java语言工具包。现在这个包已经加入处于JCP控制下的JSR标准。SUN Java SE 1.5实现中也已经加入了这部分代码,有兴趣的读者可以参阅。
文中所描述的生产者消费者问题的实例代码可与本文作者联系索取。
参考资料及进一步阅读
1) 孙钟秀,费翔林,骆斌,谢立. 操作系统教程,第三版. 高等教育出版社,2003.8
2) Abraham Silberschatz, Peter Baer Galvin, Greg Gagne. Operating System Concepts, 6th Edition. John Wiley & Sons, Inc/高等教育出版社影印, 2002.5
3) David R. Butenhof/于磊,曾刚. Programming with POSIX Threads. Addison Wesley/中国电力出版社, 2003
4) Bill Venners著/曹晓钢,蒋靖译. Inside the Java Virtual Machine, 2nd edition. McGraw-Hill/机械工业出版社, 2003
5) Ken Arnold, James Gosling, David Holmes. The Java Programming Language, 3rd Edition. Addison Wesley/中国电力出版社影印, 2003.5
6) Michael L. Scott著/裘宗燕译. Programming Language Pragmatics. Elsevier/电子工业出版社, 2005.3
关于作者
(本文可自由转载,但请给出原文链接: http://blog.csdn.net/thl789/archive/2005/12/30/566494.aspx)。
解析JVM线程同步机制相关推荐
- Java高级-线程同步机制实现
2019独角兽企业重金招聘Python工程师标准>>> 前言 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Threa ...
- Java多线程之线程同步机制(锁,线程池等等)
Java多线程之线程同步机制 一.概念 1.并发 2.起因 3.缺点 二.三大不安全案例 1.样例一(模拟买票场景) 2.样例二(模拟取钱场景) 3.样例三(模拟集合) 三.同步方法及同步块 1.同步 ...
- 01 线程同步机制封装类
01 线程同步机制封装类 RAII RAII全称是"Resource Acquisition is Initialization",直译过来是"资源获取即初始化" ...
- 对Java多线程编程的初步了解,实现多线程的三种方式以及多线程并发安全的线程同步机制
什么叫进程?什么叫线程? 进程相当于一个应用程序,线程就是进程中的一个应用场景或者说是一个执行单元,一个进程可以启动多个线程,每个线程执行不同的任务,一个线程不能单独存在,他必须是进程的一部分,当进程 ...
- Python线程同步机制: Locks, RLocks, Semaphores, Condition
为什么80%的码农都做不了架构师?>>> 翻译自Laurent Luce的博客 原文名称:Python threads synchronization: Locks, RLoc ...
- c++ linux 线程等待与唤醒_C++ Linux线程同步机制:POSIX信号量,互斥锁,条件变量...
线程同步机制:POSIX 信号量,互斥量,条件变量 POSIX 信号量 常用的POSIX 信号量函数为如下5个: sem_init sem_destroy sem_wait sem_trywait s ...
- ThreadLocal和线程同步机制的对比
ThreadLocal和线程同步都是为了解决多线程中相同变量的访问冲突问题,那么,二者的区别在哪里?和线程同步机制对比,ThreadLocal有什么优势? 同步机制中通过对象的锁机制保证同一时间只有一 ...
- 学习java的第四十天,线程的优先级、守护线程、线程同步机制、死锁
一.线程的优先级(priority) Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行. 线程的优先级用数字表示,范围1~10 Thr ...
- 线程同步机制synchronized中锁的判断以及锁的作用范围
当我们使用多个线程访问同一资源(可以是同一个变量.同一个文件.同一条记录等)的时候,若多个线程只有读操作,那么不会发生线程安全问题,但是如果多个线程中对资源有读和写的操作,就容易出现线程安全问题. 要 ...
最新文章
- Maven(七) maven 常用命令
- windows下利用IIS搭建web和ftp服务以及防火墙配置
- java jsf_将Java 8日期时间API与JSF和Java EE 7结合使用
- 数据返回nan_数据处理教程
- C++ 之父即将开始直播,请就位!
- Handler 消息传递机制
- 颜色(color)、字体(Fonts)管理(颜色的 hex 表示)
- 令牌环算法_一环(令牌)将它们全部统治
- 985翻译硕士都在找的这款软件
- raid卡组不同raid_磁盘阵列卡组RAIDO 对比主板自带组磁盘阵列
- 目标检测00-05:mmdetection(Foveabox为例)-白话给你讲论文-翻译无死角-1
- 短视频SDK接入(2)---环境搭建
- UNIX再学习 -- ps、top、kill 指令
- Qt编译出错:During startup program exited with code 0xc0000135
- 【计算机网络】湖科大学习笔记---数据链路层
- 《大数据时代:生活、工作与思维的大变革》读书笔记3(完)
- Three.js中添加指南针
- FGF21 类似物 PF-05231023 改善糖尿病并发症
- 西安阿里云代理商:vue项目部署到阿里云服务器(windows)Nginx代理
- 5亿微博用户信息泄露背后:BTC与暗网数据的纠缠
热门文章
- [Python人工智能] 三.theano实现分类神经网络及机器学习基础
- 2 0 2 0 年 第 十 一 届 蓝 桥 杯 - 国赛 - CC++大学B组 - A.美丽的2
- 快速理解https是如何保证安全的
- 【Linux系统编程】同步和互斥的概念
- 【Android】Android Service 服务
- aspx隐藏前台控件div_javascript总结--div
- java.util 常见_Java基础知识-java.util.concurrent包下常见类的使用
- 每天一道LeetCode-----判断数组中是否存在两个位置上面的值相等并且下标的差小于某个值
- C++11学习笔记-----获取异步操作执行结果
- 每天一道LeetCode-----计算给定序列中所有长度为k的滑动窗的最大值集合