先看一个问题:

有两个线程,子线程先执行10次,然后主线程执行5次,然后再切换到子线程执行10,再主线程执行5次……如此往返执行50次。

看完这个问题,很明显要用到线程间的通信了, 先分析一下思路:首先肯定要有两个线程,然后每个线程中肯定有个50次的循环,因为每个线程都要往返执行任务50次,主线程的任务是执行5次,子线程的任务是执行10次。线程间通信技术主要用到 wait() 方法和 notify() 方法。wait() 方法会导致当前线程等待,并释放所持有的锁,notify() 方法表示唤醒在此对象监视器上等待的单个线程。下面来一步步完成这道线程间通信问题。

首先不考虑主线程和子线程之间的通信,先把各个线程所要执行的任务写好:

public class TraditionalThreadCommunication {

public static void main(String[] args) {

//开启一个子线程

new Thread(new Runnable() {

@Override

public void run() {

for(int i = 1; i <= 50; i ++) {

synchronized (TraditionalThreadCommunication.class) {

//子线程任务:执行10次

for(int j = 1;j <= 10; j ++) {

System.out.println("sub thread sequence of " + j + ", loop of " + i);

}

}

}

}

}).start();

//main方法即主线程

for(int i = 1; i <= 50; i ++) {

synchronized (TraditionalThreadCommunication.class) {

//主线程任务:执行5次

for(int j = 1;j <= 5; j ++) {

System.out.println("main thread sequence of " + j + ", loop of " + i);

}

}

}

}

}

如上,两个线程各有50次大循环,执行50次任务,子线程的任务是执行10次,主线程的任务是执行5次。为了保证两个线程间的同步问题,所以用了 synchronized 同步代码块,并使用了相同的锁:类的字节码对象。这样可以保证线程安全。但是这种设计不太好,就像我在上一节的死锁中写的一样,我们可以把线程任务放到一个类中,这种设计的模式更加结构化,而且把不同的线程任务放到同一个类中会很容易解决同步问题,因为在一个类中很容易使用同一把锁。所以把上面的程序修改一下:

public class TraditionalThreadCommunication {

public static void main(String[] args) {

Business bussiness = new Business(); //new一个线程任务处理类

//开启一个子线程

new Thread(new Runnable() {

@Override

public void run() {

for(int i = 1; i <= 50; i ++) {

bussiness.sub(i);

}

}

}).start();

//main方法即主线程

for(int i = 1; i <= 50; i ++) {

bussiness.main(i);

}

}

}

//要用到的共同数据(包括同步锁)或共同的若干个方法应该归在同一个类身上,这种设计正好体现了高类聚和程序的健壮性。

class Business {

public synchronized void sub(int i) {

for(int j = 1;j <= 10; j ++) {

System.out.println("sub thread sequence of " + j + ", loop of " + i);

}

}

public synchronized void main(int i) {

for(int j = 1;j <= 5; j ++) {

System.out.println("main thread sequence of " + j + ", loop of " + i);

}

}

经过这样修改后,程序结构更加清晰了,也更加健壮了,只要在两个线程任务方法上加上 synchronized 关键字即可,用的都是 this 这把锁。但是现在两个线程之间还没有通信,执行的结果是主线程循环执行任务50次,然后子线程再循环执行任务50次,原因很简单,因为有 synchronized 同步。

下面继续完善程序,让两个线程之间完成题目中所描述的那样通信:

public class TraditionalThreadCommunication {

public static void main(String[] args) {

Business bussiness = new Business(); //new一个线程任务处理类

//开启一个子线程

new Thread(new Runnable() {

@Override

public void run() {

for(int i = 1; i <= 50; i ++) {

bussiness.sub(i);

}

}

}).start();

//main方法即主线程

for(int i = 1; i <= 50; i ++) {

bussiness.main(i);

}

}

}

//要用到共同数据(包括同步锁)或共同的若干个方法应该归在同一个类身上,这种设计正好体现了高雷剧和程序的健壮性。

class Business {

private boolean bShouldSub = true;

public synchronized void sub(int i) {

while(!bShouldSub) { //如果不轮到自己执行,就睡

try {

this.wait(); //调用wait()方法的对象必须和synchronized锁对象一致,这里synchronized在方法上,所以用this

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

for(int j = 1;j <= 10; j ++) {

System.out.println("sub thread sequence of " + j + ", loop of " + i);

}

bShouldSub = false; //改变标记

this.notify(); //唤醒正在等待的主线程

}

public synchronized void main(int i) {

while(bShouldSub) { //如果不轮到自己执行,就睡

try {

this.wait();

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

for(int j = 1;j <= 5; j ++) {

System.out.println("main thread sequence of " + j + ", loop of " + i);

}

bShouldSub = true; //改变标记

this.notify(); //唤醒正在等待的子线程

}

}

首先,先不说具体的程序实现,就从结构上来看,已经体会到了这种设计的好处了:主函数里不用修改任何东西,关于线程间同步和线程间通信的逻辑全都在 Business 类中,主函数中的不同线程只需要调用放在该类中对应的任务即可。体现了高类聚的好处。

再看一下具体的代码,首先定义一个 boolean 型变量来标识哪个线程该执行,当不是子线程执行的时候,它就睡,那么很自然主线程就执行了,执行完了,修改了 bShouldSub 并唤醒了子线程,子线程这时候再判断一下 while 不满足了,就不睡了,就执行子线程任务,同样地,刚刚主线程修改了 bShouldSub 后,第二次循环来执行主线程任务的时候,判断 while 满足就睡了,等待子线程来唤醒。这样逻辑就很清楚了,主线程和子线程你一下我一下轮流执行各自的任务,这种节奏共循环50次。

另外有个小小的说明:这里其实用 if 来判断也是可以的,但是为什么要用 while 呢?因为有时候线程会假醒(就好像人的梦游,明明正在睡,结果站起来了),如果用的是if的话,那么它假醒了后,就不会再返回去判断if了,那它就很自然的往下执行任务,好了,另一个线程正在执行呢,啪叽一下就与另一个线程之间相互影响了。但是如果是while的话就不一样了,就算线程假醒了,它还会判断一下 while 的,但是此时另一个线程在执行啊,bShouldSub 并没有被修改,所以还是进到 while 里了,又被睡了~所以很安全,不会影响另一个线程!官方 JDK 文档中也是这么干的。

线程间通信就总结到这吧~若有错误,欢迎指正,我们一起进步。

java 线程假醒_Java并发基础05. 传统线程同步通信技术相关推荐

  1. Java并发基础02. 传统线程技术中的定时器技术

    传统线程技术中有个定时器,定时器的类是Timer,我们使用定时器的目的就是给它安排任务,让它在指定的时间完成任务.所以先来看一下Timer类中的方法(主要看常用的TimerTask()方法): 前面两 ...

  2. Java并发基础01. 传统线程技术中创建线程的两种方式

    传统的线程技术中有两种创建线程的方式:一是继承Thread类,并重写run()方法:二是实现Runnable接口,覆盖接口中的run()方法,并把Runnable接口的实现扔给Thread.这两种方式 ...

  3. java 多线程共享变量两类问题_Java并发基础09. 多个线程间共享数据问题

    先看一个多线程间共享数据的问题: 设计四个线程,其中两个线程每次对data增加1,另外两个线程每次对data减少1. 从问题来看,很明显涉及到了线程间通数据的共享,四个线程共享一个 data,共同操作 ...

  4. java 并发 面试_Java 并发基础常见面试题总结

    1. 什么是线程和进程? 1.1. 何为进程? 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的.系统运行一个程序即是一个进程从创建,运行到消亡的过程. 在 Java 中,当我们启 ...

  5. java等待5秒_Java并发编程-主线程等待子线程解决方案

    主线程等待所有子线程执行完成之后,再继续往下执行的解决方案 public class TestThread extends Thread { public void run() { System.ou ...

  6. java 线程假醒_“梦中梦|连环梦|多重梦境”与“假醒”——以普通梦为视角...

    "梦中梦|多重梦境|连环梦"与"假醒"这两个概念通常放在一起比较,但多有混淆.指南君通过本文强行重构这两个概念. 一.典型的假醒案例 首先来看一段视频: 08: ...

  7. java程序使用异步总线_JAVA并发编程基础

    CPU核心 核心(Die)又称为内核,是CPU最重要的组成部分.CPU中心那块隆起的芯片就是核心,是由单晶硅以一定的生产工艺制造出来的,CPU所有的计算.接受/存储命令.处理数据都由核心执行.各种CP ...

  8. java多线程工具类_Java多线程系列之:线程的并发工具类

    一,Fork-Join 1,定义: Fork-Join框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不能再拆时),再将一个个的小任务运算的结果进行join汇总. 2, ...

  9. java 线程的理解_Java多线程基础理解

    wait: 让当前线程处于"等待(阻塞)状态","直到其他线程调用此对象的notify()方法或是notifyAll()方法",当前线程被唤醒(进入" ...

最新文章

  1. 近六成员工强烈支持,携程将推出“3+2 ”工作模式,一周三天到岗两天在家办公...
  2. P5056-[模板]插头dp
  3. 开发ffmpeg/live555常见问题错误及解决方法
  4. 遥感数据下载网站汇总
  5. 企业信息化战略规划方法
  6. 苹果电脑重装系统步骤
  7. 笔记本电脑触摸板操作
  8. 计算机报 论文,计算机学院毕业设计(论文)题目上报.doc
  9. 怎么放大整个html页面,怎么把网页的字变大?教你网页操作实用技巧【步骤详解】...
  10. 头戴式蓝牙耳机,出现左耳没有声音,右耳正常。
  11. 清差额征税和简易计税的适用情形
  12. 路由器动态ip获取不到的处理办法
  13. 基于C语言的个人所得税计税系统
  14. 计算机科学与技术有剪辑吗,计算机科学与技术学院第七届“微剪辑大赛”
  15. 编写程序读取一系列整数,找出它们的最大数,然后计算该数的出现次数,假定输入以0结束。
  16. 百事正用AI种土豆,连削皮算法都搞上了
  17. 详细到吐血 —— 树莓派驱动开发入门:从读懂框架到自己写驱动
  18. springboot学习文章
  19. 2015-2016-2 《Java程序设计》教学进程
  20. python安装要装oracle,python安装oracle数据库

热门文章

  1. linux创建的kvm无法运行,使用virt-manager运行虚拟机的方法(创建第一个虚拟机)...
  2. linux nobody 用户,Linux CentOS7安装配置tomcat8(使用非root用户/nobody用户运行)
  3. 大学计算机html,编程基础(C+VB+HTML)(辅)19级计算机
  4. springboot创建parent_Springboot 框架整理,建议做开发的都看看,整理的比较详细!...
  5. IDEA插件推荐:中文字符自动转化!
  6. 小团队 vs 大团队
  7. 不简单的 SimpleDateFormat
  8. php小程序地图处理,微信小程序 地图map详解及简单实例
  9. python类的函数_python 类函数
  10. php修改新闻分类代码,新闻分类录入、显示系统_php