并发编程,一般指多线程编程,它可以充分利用计算机的计算资源,使得一个任务可以分为几个子模块同时执行,提高程序执行速度。然而,并不是启动越多线程,就能让程序执行越快,多线程同时带来了上下文切换、多线程间的通信与同步、死锁等问题。合理的利用多线程进行编程是一件有挑战性的事。

Java 自一开始就内置了对多线程的支持,在 JDK1.5 版本中引入了 java.util.concurrent 包,让 Java 的并发编程有了更多的选择和更好的工作方式。


多线程带来的问题

看下面一个例子,猜一下它的输出结果:

public class MultiThread {static int i = 0;public static int get() {return i++;}public static void main(String[] args) {for (int t = 0; t < 10; t++) {new Thread() {public void run() {System.out.println(get());}}.start();}}
}

输出结果:

1
0
2
3
4
5
6
7
8
9

注意,输出结果是不确定的,虽然大部分输出情况都是0-9顺序输出,但总会出现“异常情况”,也就是发生了冲突,当线程越多冲突的机会就越大。

输出结果不确定的原因在于程序中开了10个线程,而这10个线程是同步执行的,可能第二个线程比第一个线程先进行输出,也就出现了上面的结果。至于为什么大部分情况都是按顺序输出,因为虽然这10个线程是同步执行的,但是它们的启动顺序不一样,第一个线程最先启动,最后一个线程最后启动。


让线程按顺序输出

如果现在有10个线程,编号为0-9,我们想让0号线程执行完再执行1号线程,以此类推,最后执行9号线程。解决的方法是使用线程的 join() 方法。

假设线程的名字叫做 t,t.join() 方法的作用是将当前线程加入 t 线程,当 t 线程执行完后再执行当前线程。

示例如下:

public class JoinDemo {public static void main(String[] args) {Thread[] threads = new Thread[10];for (int i = 0; i < 10; i++) {threads[i] = new Thread(String.valueOf(i)) {public void run() {String name = Thread.currentThread().getName();int number = Integer.parseInt(name);if (number > 0)try {threads[number - 1].join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程名:" + name);}};threads[i].start();}}
}

输出结果:

线程名:0
线程名:1
线程名:2
线程名:3
线程名:4
线程名:5
线程名:6
线程名:7
线程名:8
线程名:9

程序中我们让后一个线程加到前一个线程中,使得只有前一个线程执行完了才能执行后一个线程。

t.join() 方法内部实际上是调用了 Object的wait() 方法,因此使用 join() 方法之前,需要先获得当前对象的锁,执行 wait() 方法后会释放当前对象的锁。


等待通知机制

等待/通知机制,是指一个线程A调用了对象 O 的 wait() 方法进入等待状态,另一个线程B调用了对象 O 的 notify() 或 notifyAll() 方法通知在对象上等待的线程,使其从 wait() 状态返回。两个线程间通过对象 O 来交互。

notify() 与 notifyAll() 的区别在于前者只会通知一个在对象上等待的线程,如果在该对象上等待的线程有多个,则会随机选取一个;后者会通知所有等待在该对象中的线程。

注意,对于 notifyAll() 来说,虽然所有的线程都被通知了。但是这些线程会进行竞争,且只会有一个线程成功获取到锁,在这个线程执行完毕之前,其他的线程必须等待。但 notifyAll() 免去了线程运行完后再通知其他线程的必要,因为之前已经通知过了所有的线程。

示例如下:

public class WaitNotify {static Object lock = new Object();static class Wait implements Runnable {public void run() {synchronized (lock) {System.out.println("线程" + Thread.currentThread().getName() + "开始等待");try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("继续执行时间:" + LocalTime.now().withNano(0));}}}static class Notify implements Runnable {public void run() {synchronized (lock) {System.out.println("线程" + Thread.currentThread().getName() + "开始通知");lock.notify();System.out.println("通知时间:" + LocalTime.now().withNano(0));}}}public static void main(String[] args) {Thread wait = new Thread(new Wait(), "wait");Thread notify = new Thread(new Notify(), "notify");wait.start();notify.start();}
}

输出结果:

线程wait开始等待
线程notify开始通知
通知时间:12:20:45
继续执行时间:12:20:45

注意,执行 notify() 或 notifyAll() 方法时不会释放 lock 的锁,直到 notify 线程释放了 lock 后,wait 线程才能从 wait() 方法中返回。

Java 多线程通信相关推荐

  1. java程序假死_分析一个常见的java多线程通信问题(假死现象)

    一件复杂的事,一个人如果不能做,两个人又做的不好,一群人就可能很好的解决了.对于线程来说也是,通过多个线程就能完成一个更复杂的功能,这就需要多个线程协作,协作就需要交流,但是交流总是会出问题的.在这篇 ...

  2. java多线程通信_Java多线程-线程通信

    原标题:Java多线程-线程通信 通信的方式 要想实现多个线程之间的协同,如:线程执行先后顺序.获取某个线程执行的结果等等.涉及到线程之间的相互通信,分为下面四类: 文件共享 网络共享 共享变量 JD ...

  3. Java 多线程 通信 通道 (猫狗赛跑)

    package thread; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOut ...

  4. java线程两个汽车赛跑,Java 多线程 通信 通道 (猫狗赛跑)

    package thread; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOut ...

  5. java多线程通信方法

    http://my.oschina.net/u/248570/blog/53226 转载于:https://www.cnblogs.com/svennee/p/4081153.html

  6. Java多线程通信-CyclicBarrier(栅栏)

    一.CyclicBarrier(栅栏) 通过闭锁,我们可以启动一组相关的操作.或者等待一组相关的操作结束.闭锁是一次性对象,到达终止状态后将不可用.     CyclicBarrier与闭锁类似,能够 ...

  7. Java多线程通信Suspend和Resume,为什么被弃用,如何导致死锁

    文章目录 Suspend和Resume作用 Suspend和Resume为什么被弃用 Suspend和Resume作用 suspend: 让线程挂起,暂停,程序停止往下执行. resume: 唤醒被s ...

  8. java多线程三之线程协作与通信实例

    多线程的难点主要就是多线程通信协作这一块了,前面笔记二中提到了常见的同步方法,这里主要是进行实例学习了,今天总结了一下3个实例: 1.银行存款与提款多线程实现,使用Lock锁和条件Condition. ...

  9. Java 多线程(七) 线程间的通信

    Java 多线程(七) 线程间的通信--wait及notify方法 线程间的相互作用 线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务. Object类中相关的方法有两个notify方法 ...

最新文章

  1. FreeModbus 移植于STM32 实现Modbus RTU通信
  2. 带你深度解析Maven
  3. Linux中的MySql数据库远程连接
  4. rewirte 规则
  5. 怎么在css中加横线分层,CSS分层
  6. Windows 上配置Docker Desktop 的k8s
  7. 写一个方法判断一个字符串是否对称_判断一个男生是否好色的方法
  8. HDU 3488 KM
  9. Linux Shell脚本_禁止定时任务发送邮件
  10. 简述Qt编程中遇到的编码格式问题
  11. 【Android】Windows环境安装Android Studio教程
  12. Topcoder SRM 637 (Div.2)
  13. Java多线程-生产者消费者问题(多个消费者多个生产者)
  14. Base64压缩UUID长度替换Hibernate原有UUID生成器
  15. 啊哈C语言 第四章 第 十 节
  16. excel制作(1)多记录跟进表格
  17. 下载频道2013上半年超人气精华资源汇总
  18. nginx防盗链配置
  19. python实战—考勤报表数据分析处理
  20. Fabric系列 - 锚节点

热门文章

  1. SwiftUI初级课程(二)Text控件的段落和填充属性
  2. java BufferedImage(图片内存缓冲区)——批量加水印
  3. 初学者,FreeRTOS学习记录,配合STM32CubeMX(一)
  4. 如何劝说出国留学的孩子完成学业后回国
  5. 华为一出鸿蒙开 鲲鹏展翅九万里,科技 _ 不止“鸿蒙”,还有子龙、浩天、青鸟、朱雀……华为简直注册了整本《山海经》!中国人有多浪漫,看这些名字就知道了...
  6. pearson相关系数与spearman相关系数
  7. 香蕉派(Banana Pi) BPI-M2 Zero 评测试,与树莓派 Zero同尺寸的开发板
  8. linux shell permission denied,Linux系统出现permission denied问题解决措施
  9. kindeditor批量上传设置_ftp如何使用,ftp如何使用,3步掌握安装及使用方法 - 批量远程桌面管理服务器、vps教程...
  10. [OpenCL] 内核编程:数据类型和设备内存(13)