为什么需要线程间通信

让线程之间合作,提高运行效率。

volatile和synchronized关键字

实现原理

这两个方式都是采用共享内存的方式进行通信,通过同步机制保证数据可见性和排他性。

特点

  • 本质是数据共享。
  • 不能特定地传给某个线程数据,需要程序员自己编写逻辑。数据在各个线程的更新顺序由操作系统决定。
  • 不能过多使用,这样会降低程序执行效率。

使用案例

// 用于控制线程当前的执行状态
private volatile boolean flag = false; // 开启一条线程
Thread t1 = new Thread(new Runnable(){
void run(){
// 开关
while(!flag){
Thread.sleep(1000);
}
// 执行线程任务
doSometing();
}
}).start(); // 开始执行
public void start(){
flag = true;
}

这里通过volatile修饰的标志位flag实现其他线程对t1的控制。

等待和通知机制

等待/通知是Java实现多个进程交替协作的机制。

实现方法

Java提供了API实现该机制。

使用注意

  • 上述所有方法都必须放在一个同步块中。
  • 上述方法都只能由所处同步块的锁对象调用。
  • 调用notify()方法之后,只是将线程从等待队列中转移到阻塞队列,当线程得到锁之后,才能用wait()方法之后,继续执行程序。

经典范式

该范式分为两个部分,分别针对等待方(消费者)和通知方(生产者)。

生产者通过notify唤醒消费者。消费者通过wait等待生产者生产完毕。

生产者:

private volatile boolean flag = false;

synchronized(objectA) {
flag = true;
objectA.notify();
}

消费者:

synchronized(objectA) {
while(!flag) {
objectA.wait();
} doSomething();
}

超时等待

为了避免等待方无止境地等待,可以通过超时等待,跳出等待。

public void get(long mills){
synchronized( list ){
// 不加超时功能
if ( mills <= 0 ) {
while( list.isEmpty() ){
list.wait();
}
} // 添加超时功能
else {
boolean isTimeout = false;
while(list.isEmpty() && isTimeout){
list.wait(mills);
isTimeout = true;
} // doSometing……
}
}
}

代码中利用list作为中间变量装载数据。

管道流

什么是管道流

用于线程间的数据传输。传输媒介为内存。

实现方式

管道输入输出主要包括四种具体实现:PipedOutputStream、PipedInputStream、PipedWriter、PipedReader。分别对应字节流和字符流。

// 创建输入流与输出流对象
PipedWriter out = new PipedWriter();
PipedReader in = new PipedReader(); // 连接输入输出流
out.connect(in); // 创建写线程
class WriteThread extends Thread{
private PipedWriter out; public WriteThread(PipedWriter out){
this.out = out;
} public void run(){
out.write("lippon");
}
} // 创建读线程
class ReaderThread extends Thread{
private PipedReader in; public ReaderThread(PipedReader in){
this.in = in;
} public void run(){
in.read();
}
}

Thread.join

作用

  1. 让多个并发的线程串行执行。
  2. 当A线程调用B.jion(),那么,A会等待B结束后,再从jion继续执行。
  3. 如果被等待的线程执行很长的时间,那么join会抛出InterruptedException。当调用B.interrupt()之后,jion函数就会抛出异常。

实现原理

  1. join的底层采用wait() 方法进行停止运行。
  2. 然后当被等待线程终止后,会调用线程的notifyAll() 方法释等待队列中的线程。

实现方式

public static void main(String[] args){

    // 开启一条线程
Thread t = new Thread(new Runnable(){
public void run(){
// doSometing
}
}).start(); // 调用join,等待t线程执行完毕
try{
t.join();
}catch(InterruptedException e){
// 中断处理……
}
}

Java并发编程的艺术(七)——线程间的通信的更多相关文章

  1. Java并发编程的艺术(六)——线程间的通信

    多条线程之间有时需要数据交互,下面介绍五种线程间数据交互的方式,他们的使用场景各有不同. 1. volatile.synchronized关键字 PS:关于volatile的详细介绍请移步至:Java ...

  2. java并发编程(十一)线程间的通信notify通知的遗漏

    notify通知的遗漏很容易理解,即threadA还没开始wait的时候,threadB已经notify了,这样,threadB通知是没有任何响应的,当threadB退出synchronized代码块 ...

  3. Java并发编程的艺术(七)——Executors

    Executors框架简介 Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动.执行和关闭,可以简化并发 ...

  4. Java并发编程(八)线程间协作(上)

    多线程并发执行时,不同的线程执行的内容之间可能存在一些依赖关系,比如线程一执行a()方法和c()方法,线程二执行b()方法,方法a()必须在方法b()之前执行,而方法c()必须在方法b()之后执行.这 ...

  5. Java并发编程(十三)线程间协作的两种方式:wait、notify、notifyAll和Condition

    在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权.因为生产者如果 ...

  6. Java并发编程(九)线程间协作(下)

    上篇我们讲了使用wait()和notify()使线程间实现合作,这种方式很直接也很灵活,但是使用之前需要获取对象的锁,notify()调用的次数如果小于等待线程的数量就会导致有的线程会一直等待下去.这 ...

  7. Java并发编程的艺术(十一)——线程池(2)

    Executor两级调度模型 在HotSpot虚拟机中,Java中的线程将会被一一映射为操作系统的线程. 在Java虚拟机层面,用户将多个任务提交给Executor框架,Executor负责分配线程执 ...

  8. Java并发编程的艺术(四)——线程的状态

    线程的状态 初始态:NEW 创建一个Thread对象,但还未调用start()启动线程时,线程处于初始态. 运行态:RUNNABLE 在Java中,运行态包括就绪态 和 运行态. 就绪态 该状态下的线 ...

  9. Java并发编程的艺术(十)——线程池(1)

    线程池的作用 减少资源的开销 减少了每次创建线程.销毁线程的开销. 提高响应速度 每次请求到来时,由于线程的创建已经完成,故可以直接执行任务,因此提高了响应速度. 提高线程的可管理性 线程是一种稀缺资 ...

  10. Java并发编程的艺术(十)——线程池

    线程池的作用 降低资源消耗.重复利用已有线程,减少线程的创建和销毁造成的消耗. 提高响应速度.当有任务需要处理的时候,就不用再花费重新创建线程的时间了. 提高线程的可管理性.不合理利用线程,会浪费资源 ...

随机推荐

  1. python3中的os.path模块

    os.path模块主要用于获取文件的属性,这里对该模块中一些常用的函数做些记录. os.abspath(path):获取文件的绝对路径.这里path指的是路径,例如我这里输入"data.cs ...

  2. Java编码和字符集(详解)

    [1]什么是编码? [2]通过生活案例: [3]由权威机构形成的编码表才可以称之为:字符集 ASCII 英文字符集 用一个字节的7位表示 IOS8859-1 西欧字符集 用一个字节的8位表示 GB23 ...

  3. MySQL索引背后的数据结构及原理

    摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BT ...

  4. Spring MVC——项目的开发流程

    创建项目(IDEA下) 打开IDEA,我们开始创建一个简单的Spring MVC项目,流程如下: 这里要注意一下,我们是基于Maven开发项目,当然是要配置Maven环境的,如果大家之前从来没有配置过 ...

  5. tp5配置引入使用redis

    1.首先你的php得是已经安装了redis扩展的 2.在tp里找到config.php配置文件,找到cache,改成下面的样子 'cache' => [ // 选择模式 'type' => ...

  6. 基于Python+requests搭建的自动化框架-实现流程化的接口串联

    框架产生目的:公司走的是敏捷开发模式,编写这种框架是为了能够满足当前这种发展模式,用于前后端联调之前(后端开发完接口,前端还没有将业务处理完毕的时候)以及日后回归阶段,方便为自己腾出学(mo)习(yu ...

  7. 【ACwing 100】InDec序列——差分

    (题面来自AcWing) 给定一个长度为 n 的数列 a1,a2,-,an,每次可以选择一个区间 [l,r],使下标在这个区间内的数都加一或者都减一. 求至少需要多少次操作才能使数列中的所有数都一样, ...

  8. Forethought Future Cup - Final Round (Onsite Finalists Only) C. Thanos Nim 题解(博弈+思维)

    题目链接 题目大意 给你n堆石子(n为偶数),两个人玩游戏,每次选取n/2堆不为0的石子,然后从这n/2堆石子中丢掉一些石子(每一堆丢弃的石子数量可以不一样,但不能为0),若这次操作中没有n/2堆不为 ...

  9. Java蓝桥杯——贪心算法

    贪心算法 贪心算法:只顾眼前的苟且. 即在对问题求解时,总是做出在当前看来是最好的选择 如买苹果,专挑最大的买. 最优装载问题--加勒比海盗 货物重量:Wi={4,10,7,11,3,5,14,2} ...

  10. limit+orderBy陷阱

    碰到一个奇怪的bug,在排序的时候对一个非唯一的字段做order by, 结果在分页的时候,发现每一页的数据都是乱的,并不是对整个排序之后再去limit的. 当我们用唯一字段去排序的时候,因为在找到满 ...