Java Thread系列(四)线程通信
Java Thread系列(四)线程通信
一、传统通信
public static void main(String[] args) {
//volatile实现两个线程间数据可见性
private volatile static List list = new ArrayList();
Thread t1 = new Thread(new Runnable() { // (1)
public void run() {
try {
for(int i = 0; i <10; i++){
list.add(i);
System.out.println(Thread.currentThread().getName() + "线程添加第" + (i + 1) + "个元素..");
Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1");
Thread t2 = new Thread(new Runnable() { // (2)
public void run() {
while(true){
if(list.size() == 5){
//do something
throw new RuntimeException(Thread.currentThread().getName() +
"线程接到通知 size = " + list.size() + " 线程停止..");
}
}
}
}, "t2");
t1.start();
t2.start();
}
t1 线程不断将生产的数据放入 list 集合中
t2 线程开启 while 循环监听 t1 线程,虽然可以实现 list.size()==5 时实时通知 t2 线程,但太浪费性能,考虑用
await/notify提高性能,程序执行结果如下:
t1线程添加第1个元素..
t1线程添加第2个元素..
t1线程添加第3个元素..
t1线程添加第4个元素..
t1线程添加第5个元素..
Exception in thread "t2" java.lang.RuntimeException: t2线程接到通知 size = 5 线程停止..
at com.github.binarylei.thread._2_1conn.ListAdvice1$2.run(ListAdvice1.java:35)
at java.lang.Thread.run(Thread.java:745)
t1线程添加第6个元素..
t1线程添加第7个元素..
t1线程添加第8个元素..
t1线程添加第9个元素..
t1线程添加第10个元素..
二、wait/notify 实现通信
/**
* 使用wait/notify方法实现线程单挑通信(注意这两个方法是Object类的方法)
* 1. wait和notity必须配合synchronized关键字使用
* 2. wait方法(关闭线程)释放锁,notify(唤醒线程)方法不释放锁
* 缺点:通知不实时,使用CountDownLatch实现实时通知
*/
public static void main(String[] args) {
private volatile static List list = new ArrayList();
final Object lock = new Object();
Thread t1 = new Thread(new Runnable() { // (1)
public void run() {
try {
synchronized (lock) {
System.out.println("t1启动..");
for(int i = 0; i <10; i++){
list.add(i);
System.out.println(Thread.currentThread().getName() + "线程添加第" + (i + 1) + "个元素..");
Thread.sleep(500);
if(list.size() == 5){
System.out.println("已经发出通知..");
lock.notify();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1");
Thread t2 = new Thread(new Runnable() { // (2)
public void run() {
synchronized (lock) {
System.out.println("t2启动..");
if(list.size() != 5){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//do something
throw new RuntimeException(Thread.currentThread().getName() +
"线程接到通知 size = " + list.size() + " 线程停止..");
}
}
}, "t2");
}
t1 线程当
list.size()==5时lock.notify()唤醒 t2 线程,注意wait/notify必须配合synchronized使用。t2 线程调用
lock.wait()后处于一直阻塞状态,直到 t1 线程调用lock.notify()唤醒该线程,倘若没有线程唤醒 t2 线程,那么 t2 线程就一直处于阻塞状态。本例中若 t1 线程先启动,那么 t2 线程调用lock.wait()就永远阻塞无法执行。程序执行结果如下:。
t2启动..
t1启动..
t1线程添加第1个元素..
t1线程添加第2个元素..
t1线程添加第3个元素..
t1线程添加第4个元素..
t1线程添加第5个元素..
已经发出通知..
t1线程添加第6个元素..
t1线程添加第7个元素..
t1线程添加第8个元素..
t1线程添加第9个元素..
t1线程添加第10个元素..
Exception in thread "t2" java.lang.RuntimeException: t2线程接到通知 size = 10 线程停止..
at com.github.binarylei.thread._2_1conn.ListAdd2$2.run(ListAdd2.java:51)
at java.lang.Thread.run(Thread.java:745)
- 由于 t1 线程
lock.notify()后不会释放锁,t2 线程虽然被唤醒但不能获取锁,所以通知就不那么实时,只有等 t1 线程执行完成释放锁后 t2 线程才能获得锁执行相应操作,解决方案:使用CountDownLatch
三、CountDownLatch 实现实时通信
public static void main(String[] args) {
private volatile static List list = new ArrayList();
final CountDownLatch countDownLatch = new CountDownLatch(1); // (1)
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
System.out.println("t1启动..");
for(int i = 0; i <10; i++){
list.add(i);
System.out.println(Thread.currentThread().getName() + "线程添加第" + (i + 1) + "个元素..");
Thread.sleep(500);
if(list.size() == 5){
System.out.println("已经发出通知..");
countDownLatch.countDown(); // (2)
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
public void run() {
System.out.println("t2启动..");
if(list.size() != 5){
try {
countDownLatch.await(); // (3)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//do something
throw new RuntimeException(Thread.currentThread().getName() +
"线程接到通知 size = " + list.size() + " 线程停止..");
}
}, "t2");
t1.start();
t2.start();
}
CountDownLatch 同步工具类,允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行,参数 1 表示需要等待的线程数量,具体来说就是参数为几就必须调用几次
countDownLatch.countDown()countDownLatch.countDown()唤醒线程countDownLatch.await()阻塞线程,程序执行结果如下:
t1启动..
t1线程添加第1个元素..
t2启动..
t1线程添加第2个元素..
t1线程添加第3个元素..
t1线程添加第4个元素..
t1线程添加第5个元素..
已经发出通知..
Exception in thread "t2" java.lang.RuntimeException: t2线程接到通知 size = 5 线程停止..
t1线程添加第6个元素..
at com.github.binarylei.thread._2_1conn.ListAdd3$2.run(ListAdd3.java:47)
at java.lang.Thread.run(Thread.java:745)
t1线程添加第7个元素..
t1线程添加第8个元素..
t1线程添加第9个元素..
t1线程添加第10个元素..
四、ThreadLocal
ThreadLocal 是线程局部变量,是一种多线程间并发访问变量的无锁解决方案。
ThreadLocal 和 synchronized 比较?
与 synchronized 等加锁的方式不同,ThreadLocal 完全不提供锁,而使用以空间换时间的手段,为每个线程提供变量的独立副本,以保障线程安全。
从性能上说,ThreadLocal 不具有绝对的优势,在并发不是很高的时候,加锁的性能会更好,但作为一套无锁的解决方案,在高并发量或者竞争激烈的场景,使用 ThreadLocal 可以在一定程度上减少锁竞争。
public static void main(String[] args) throws InterruptedException {
final ThreadLocal<String> th = new ThreadLocal<String>();
Thread t1 = new Thread(new Runnable() {
public void run() {
th.set("张三");
System.out.println(th.get()); // => "张三"
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
th.set("李四");
System.out.println(th.get()); // => "李四"
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t2");
t1.start(); //t1:张三
t2.start(); //t2:李四
}
五、自定义同步类窗口-Queue
Java 提供了一些同步类容器,它们是 线程安全 的,如 Vector、HashTable 等。这些同步类容器是由 Collections.synchronizedMap 等工厂方法去创建实现的,底层使用 synchronized 关键字,每次只有一个线程访问容器。下面实现一个自己的同步类窗口。
import java.util.LinkedList;
public class MyQueue {
private LinkedList list = new LinkedList();
private int max = 5;
private int min = 1;
private Object lock = new Object();
public void put(Object obj) { // (1)
synchronized (lock) {
while (list.size() == max) {
try {
lock.wait();
} catch (InterruptedException e) {
;
}
}
list.add(obj);
lock.notify();
System.out.println("put元素:" + obj);
}
}
public Object take() { // (2)
Object obj;
synchronized (lock) {
while (list.size() == min) {
try {
lock.wait();
} catch (InterruptedException e) {
;
}
}
obj = list.removeFirst();
lock.notify();
System.out.println("take元素:" + obj);
}
return obj;
}
}
测试
public static void main(String[] args) {
final MyQueue myQueue = new MyQueue();
myQueue.put("a");
myQueue.put("b");
myQueue.put("c");
myQueue.put("d");
myQueue.put("e");
new Thread(new Runnable() {
@Override
public void run() {
myQueue.put("f");
myQueue.put("g");
myQueue.put("h");
myQueue.put("i");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
myQueue.take();
myQueue.take();
myQueue.take();
}
}).start();
}
每天用心记录一点点。内容也许不重要,但习惯很重要!
Java Thread系列(四)线程通信的更多相关文章
- Java Thread系列(二)线程状态
Java Thread系列(二)线程状态 一.线程的五种状态 新建状态(New):新创建了一个线程对象,尚未启动. 就绪状态(Runnable):也叫可运行状态.线程对象创建后,其他线程调用了该对象的 ...
- Java多线程系列--“JUC线程池”05之 线程池原理(四)
概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...
- Java Thread系列(三)线程安全
Java Thread系列(三)线程安全 一.什么是线程安全 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的. 线程安全来 ...
- Java Thread系列(一)线程创建
Java Thread系列(一)线程创建 Java 中创建线程主要有三种方式:继承 Thread.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实 ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- Java Thread系列(十)Future 模式
Java Thread系列(十)Future 模式 Future 模式适合在处理很耗时的业务逻辑时进行使用,可以有效的减少系统的响应时间,提高系统的吞吐量. 一.Future 模式核心思想 如下的请求 ...
- Java多线程系列--“JUC线程池”02之 线程池原理(一)
概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...
- Java多线程系列--“JUC线程池”03之 线程池原理(二)
概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...
- Java Thread系列(五)synchronized
Java Thread系列(五)synchronized synchronized锁重入 关键字 synchronized 拥有锁重入的功能,也就是在使用 synchronized 时,当线程等到一个 ...
随机推荐
- FastAdmin 升级后出现 is already in use
FastAdmin 升级后出现 is already in use 升级 FastAdmin 改进很多,但全新安装出现以下错误 Cannot use app\common\library\Menu a ...
- php 取数组最后一个元素
可以用end()函数取出数组的最后一个元素, $rList = array( array('id'=>1,'txt'=>'a'), array('id'=>2,'txt'=>' ...
- 数据抽取 CDC
什么是数据抽取 数据抽取是指从源数据源系统抽取目的数据源系统需要的数据.实际应用中,数据源较多采用的是关系数据库. [编辑] 数据抽取的方式 (一) 全量抽取 全量抽取类似于数据迁移或数据复制,它将数 ...
- sysbench 0.5使用手册
注意:本文刚开始只介绍了sysbench 0.5之前的版本,在了解了sysbench 0.5之后进行了补充,大部分测试和参数都是一样的,只是sysbench 0.5 在测试数据库方面更加全面丰富. 关 ...
- Oracle约束详解
一 约束的定义 约束是强加在表上的规则或条件.确保数据库满足业务规则.保证数据的完整性.当对表进行DML或DDL操作时,如果此操作会造成表中的数据违反约束条件或规则的话,系统就会拒绝执行这个操作.约束 ...
- jython研究笔记
jython目前只支持python2,不支持python3. python中使用第三方包的话需要重新设置lib的地址. public void getHtmlByTxt(String path) { ...
- Boost C++ 库 中文教程(全)
Boost C++ 库 目录 第 1 章 简介 第 2 章 智能指针 第 3 章 函数对象 第 4 章 事件处理 第 5 章 字符串处理 第 6 章 多线程 第 7 章 异步输入输出 第 8 章 进程 ...
- ubuntu安装了mysql 但是编译报错 mysql.h: No such file or directory
在Ubuntu体系中,已经安装了mysql,即应用sudo apt-get install mysql-server mysql-client 但是用C编译mysql数据库时,报错fatal erro ...
- 一、jdk工具之jps(JVM Process Status Tools)命令使用
目录 一.jdk工具之jps(JVM Process Status Tools)命令使用 二.jdk命令之javah命令(C Header and Stub File Generator) 三.jdk ...
- LAMP的安装和注意事项
LAMP--Linux+Apache(httpd)+MySQL+PHP,是常用的web服务器架构,下面接受编译安装的过程,以及出现的错误. 注意事项: 1. 扩展epel源:参照:http://www ...