线程通信用来保证线程协调运行,一般在做线程同步的时候才需要考虑线程通信的问题。

1、传统的线程通信

通常利用Objeclt类提供的三个方法:

wait() 导致当前线程等待,并释放该同步监视器的锁定,直到其它线程调用该同步监视器的notify()或者notifyAll()方法唤醒线程。

notify(),唤醒在此同步监视器上等待的线程,如果有多个会任意选择一个唤醒

notifyAll() 唤醒在此同步监视器上等待的所有线程,这些线程通过调度竞争资源后,某个线程获取此同步监视器的锁,然后得以运行。

这三个方法必须由同步监视器对象调用,分为两张情况:

同步方法时,由于同步监视器为this对象,所以可以直接调用这三个方法。

示例如下:

public class SyncMethodThreadCommunication {
static class DataWrap{
int data = 0;
boolean flag = false; public synchronized void addThreadA(){
if (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} data++;
System.out.println(Thread.currentThread().getName() + " " + data);
flag = true;
notify();
} public synchronized void addThreadB() {
if (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} data++;
System.out.println(Thread.currentThread().getName() + " " + data);
flag = false;
notify();
}
} static class ThreadA extends Thread {
private DataWrap data; public ThreadA(DataWrap dataWrap) {
this.data = dataWrap;
} @Override
public void run() {
for (int i = 0; i < 10; i++) {
data.addThreadA();
}
}
} static class ThreadB extends Thread {
private DataWrap data; public ThreadB(DataWrap dataWrap) {
this.data = dataWrap;
} @Override
public void run() {
for (int i = 0; i < 10; i++) {
data.addThreadB();
}
}
} public static void main(String[] args) {
//实现两个线程轮流对数据进行加一操作
DataWrap dataWrap = new DataWrap(); new ThreadA(dataWrap).start();
new ThreadB(dataWrap).start();
} }

同步代码块时,需要使用监视器对象调用这三个方法。

示例如下:

public class SyncBlockThreadComminication {
static class DataWrap{
boolean flag;
int data;
} static class ThreadA extends Thread{
DataWrap dataWrap; public ThreadA(DataWrap dataWrap){
this.dataWrap = dataWrap;
} @Override
public void run() {
for(int i = 0 ; i < 10; i++) {
synchronized (dataWrap) {
if (dataWrap.flag) {
try {
dataWrap.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} dataWrap.data++;
System.out.println(getName() + " " + dataWrap.data);
dataWrap.flag = true;
dataWrap.notify();
}
}
}
} static class ThreadB extends Thread{
DataWrap dataWrap; public ThreadB(DataWrap dataWrap){
this.dataWrap = dataWrap;
} @Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (dataWrap) {
if (!dataWrap.flag) {
try {
dataWrap.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} dataWrap.data++;
System.out.println(getName() + " " + dataWrap.data);
dataWrap.flag = false;
dataWrap.notify();
}
}
} }
public static void main(String[] args) {
//实现两个线程轮流对数据进行加一操作 DataWrap dataWrap = new DataWrap();
new ThreadA(dataWrap).start();
new ThreadB(dataWrap).start();
} }

2、使用Condition控制线程通信

当使用Lock对象保证同步时,则使用Condition对象来保证协调。

示例如下:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import com.sun.media.sound.RIFFInvalidDataException; import javafx.scene.chart.PieChart.Data; public class SyncLockThreadCommunication {
static class DataWrap {
int data;
boolean flag; private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition(); public void addThreadA() {
lock.lock();
try {
if (flag) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
} data++;
System.out.println(Thread.currentThread().getName() + " " + data);
flag = true;
condition.signal();
} finally {
lock.unlock();
}
} public void addThreadB() {
lock.lock();
try {
if (!flag) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
} data++;
System.out.println(Thread.currentThread().getName() + " " + data);
flag = false;
condition.signal();
} finally {
lock.unlock();
}
}
} static class ThreadA extends Thread{
DataWrap dataWrap; public ThreadA(DataWrap dataWrap) {
this.dataWrap = dataWrap;
} @Override
public void run() {
for (int i = 0; i < 10; i++) {
dataWrap.addThreadA();
}
}
} static class ThreadB extends Thread{
DataWrap dataWrap; public ThreadB(DataWrap dataWrap) {
this.dataWrap = dataWrap;
} @Override
public void run() {
for (int i = 0; i < 10; i++) {
dataWrap.addThreadB();
}
}
} public static void main(String[] args) {
//实现两个线程轮流对数据进行加一操作 DataWrap dataWrap = new DataWrap();
new ThreadA(dataWrap).start();
new ThreadB(dataWrap).start();
} }

其中Condition对象的await(), singal(),singalAll()分别对应wait(),notify()和notifyAll()方法。

3、使用阻塞队列BlockingQueue控制线程通信

BlockingQueue是Queue接口的子接口,主要用来做线程通信使用,它具有一个特征:当生产者线程试图向BlockingQueue中放入元素时,如果队列已满,则该线程被阻塞;当消费者线程试图从BlockingQueue中取出元素时,如果队列已空,则该线程被阻塞。这两个特征分别对应两个支持阻塞的方法,put(E e)和take()

示例如下:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; public class BlockingQueueThreadComminication {
static class DataWrap{
int data;
} static class ThreadA extends Thread{
private BlockingQueue<DataWrap> blockingQueue; public ThreadA(BlockingQueue<DataWrap> blockingQueue, String name) {
super(name);
this.blockingQueue = blockingQueue;
} @Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
DataWrap dataWrap = blockingQueue.take(); dataWrap.data++;
System.out.println(getName() + " " + dataWrap.data);
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} static class ThreadB extends Thread{
private BlockingQueue<DataWrap> blockingQueue;
private DataWrap dataWrap; public ThreadB(BlockingQueue<DataWrap> blockingQueue, DataWrap dataWrap, String name) {
super(name);
this.blockingQueue = blockingQueue;
this.dataWrap = dataWrap;
} @Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
dataWrap.data++;
System.out.println(getName() + " " + dataWrap.data);
blockingQueue.put(dataWrap);
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} public static void main(String[] args) {
///实现两个线程轮流对数据进行加一操作 DataWrap dataWrap = new DataWrap();
BlockingQueue<DataWrap> blockingQueue = new ArrayBlockingQueue<>(1); new ThreadA(blockingQueue, "Consumer").start();
new ThreadB(blockingQueue, dataWrap, "Producer").start();
} }

BlockingQueue共有五个实现类:

ArrayBlockingQueue 基于数组实现的BlockingQueue队列

LinkedBlockingQueue 基于链表实现的BlockingQueue队列

PriorityBlockingQueue 中元素需实现Comparable接口,其中元素的排序是按照Comparator进行的定制排序。

SynchronousQueue 同步队列,要求对该队列的存取操作必须是交替进行。

DelayQueue 集合元素必须实现Delay接口,队列中元素排序按照Delay接口方法getDelay()的返回值进行排序。

Java 线程通信的更多相关文章

  1. 第23章 java线程通信——生产者/消费者模型案例

    第23章 java线程通信--生产者/消费者模型案例 1.案例: package com.rocco; /** * 生产者消费者问题,涉及到几个类 * 第一,这个问题本身就是一个类,即主类 * 第二, ...

  2. Java线程通信

    Java线程通信 螣蛇乘雾,终为土灰. 多个线程协同工作完成某个任务时就会涉及到线程间通信问题.如何使各个线程之间同时执行,顺序执行.交叉执行等. 一.线程同时执行 创建两个线程a和b,两个线程内调用 ...

  3. java线程通信与协作小结 多线程中篇(十六)

      在锁与监视器中我们对Object中的方法进行了简单介绍 以监视器原理为核心,三个方法:wait,notify.notifyAll,可以完成线程之间的通信 当然,不会像“语言”似的,有多种多样的沟通 ...

  4. Java线程通信-生产者消费者问题

    线程通信示例——生产者消费者问题 这类问题描述了一种情况,假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中的产品取走消费.假设仓库中没有产品,则生产者可以将 产品放入仓库,有 ...

  5. Java线程通信——wait() 和 notify()

    Object类中有关线程通信的方法有两个notify方法和三个wait方法,官方解释: void notify() Wakes up a single thread that is waiting o ...

  6. JAVA线程通信之生产者与消费者

    package cn.test.hf.test3; import java.util.concurrent.locks.Condition;import java.util.concurrent.lo ...

  7. Java线程入门第二篇

    Java线程通信方法 0.(why)每个线程都有自己的栈空间,我们要线程之间进行交流,合作共赢. 1.synchronized和volatile关键字 a)  看下面的synchronized关键字 ...

  8. java多线程-线程通信

    线程通信的目标是使线程间能够互相发送信号.另一方面,线程通信使线程能够等待其他线程的信号. 通过共享对象通信 忙等待 wait(),notify()和 notifyAll() 丢失的信号 假唤醒 多线 ...

  9. Java核心知识点学习----使用Condition控制线程通信

    一.需求 实现线程间的通信,主线程循环3次后,子线程2循环2次,子线程3循环3次,然后主线程接着循环3次,如此循环3次. 即:A->B->C---A->B->C---A-> ...

随机推荐

  1. C#--静态成员的生命周期

  2. BUG集锦

    1.0 jQuery 序列化表单数据 serialize() 得到的是 ""; 表单元素没有name属性.(用的美工的页面,没注意这个细节.)

  3. .NET Core HtmlAgilityPack HTML解析利器

    最近学习.NET Core ,想把自己之前的一个项目升级到 .NET Core. 发现HtmlAgilityPack 没法进行引用,遂自己做了些修改,可以运行在 .NET Core 中.现在分享出来, ...

  4. Vimium 下载 像个 Geek 一样去浏览

    插件地址(被墙):https://chrome.google.com/webstore/detail/dbepggeogbaibhgnhhndojpepiihcmeb 本地下载:http://file ...

  5. [调整] Firemonkey TEdit 避免按下立即弹出键盘的问题

    目前 Firemonkey 有一个问题,就是在 Edit 按下会立即弹出键盘,在红鱼儿的博客也提交了 QC: http://blog.sina.com.cn/s/blog_44fa172f0102vy ...

  6. 【整理】 JavaScript模块化规范AMD 和 CMD 的区别有哪些?

    根据玉伯等人在知乎上的回答整理.整理中... AMD 规范在这里:https://github.com/amdjs/amdjs-api/wiki/AMD CMD 规范在这里:https://githu ...

  7. Java反编译插件JODE介绍

    编程入门级博客:(大牛请直接忽略) 1.编程没有捷径,只有多谢代码.手动敲代码,才是最好的学习方法.写给自己!(配置Eclipse General选项:Appearance:Code Assist:) ...

  8. GJM :自定义基于 VLC 的视频播放器 [转载]

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

  9. Properties+重温Map+本地计数器

    昨天想写一个记账本,发现并不能把项目名称与内容关联起来,于是乎我想到了map,可是又不知道map储存到文件中又怎么读出来,幸好今天遇到了properties Properties是Hashtable的 ...

  10. Electron - 创建跨平台的桌面客户的应用程序

    Electron 框架的前身是 Atom Shell,可以让你写使用 JavaScript,HTML 和 CSS 构建跨平台的桌面应用程序.它是基于io.js 和 Chromium 开源项目,并用于在 ...