1、wait()/notify()

Object类中相关的方法有notify和wait方法,又因为它俩被定义在Object类中,故会被所有的类继承。它俩都是final的,不能被重写,不能通过子类重写改变。

wait()方法

让当前线程进入等待,并释放锁
注意:(1)当前线程必须拥有当前对象的monitor,也就是lock锁。这样才能调用wait()方法,否则会抛出异常
(2)线程调用wait方法,释放它对锁的拥有权,然后等待另外的线程来通知它(notify或notifyAll方法),这样他才能重新获得锁的拥有权和恢复执行
(3)要确保调用wait方法时拥有锁,即必须在同步方法或同步代码块中被调用

wait(long)方法

让当前线程进入等待,并释放锁。等待时间为long,超过这个时间没有唤醒当前线程,就自动唤醒。

notify()方法

让当前线程通知那些正处于等待状态的线程,当前线程执行完毕后释放锁,并从其他线程中唤醒一个,继续执行
注意:(1)若多个线程在等待,只有一个会被选择唤醒,选择是随意的,跟具体实现有关
(2)线程被唤醒也不能执行,必须等到当前线程释放了这个对象的锁

notifyAll()方法

让当前线程通知那些正处于等待状态的线程,当前线程执行完毕后释放锁,唤醒所有等待状态的线程

wait()和sleep()区别

线程调用wait方法,就会释放掉对象的锁。
Thread.sleep会导致线程睡眠指定的毫秒数,睡觉的时候是不会释放锁的。

wait()和notify()协作的注意事项

a.通知过早
如果通知过早,就会打乱程序的执行逻辑。

public class MyRun {private String lock = new String("");public Runnable runnableA = new Runnable() {@Overridepublic void run() {try {synchronized (lock){System.out.println("开始wait");lock.wait();System.out.println("结束wait");}}catch (InterruptedException e){e.printStackTrace();}}};public Runnable runnableB = new Runnable() {@Overridepublic void run() {synchronized (lock){System.out.println("开始notify");lock.notify();System.out.println("结束notify");}}};public static void main(String []args) throws InterruptedException{MyRun run = new MyRun();Thread threadB = new Thread(run.runnableB);threadB.start();threadB.sleep(100);Thread threadA = new Thread(run.runnableA);threadA.start();}
}

notify()先执行,会使得wait()释放锁之后,进入等待状态,永远无法被唤醒。

开始notify
结束notify
开始wait

b.等待wait的条件发生变化
再使用wait/notify模式时,wait等待条件发生改变,也会导致程序逻辑混乱
Add类执行加法操作,然后通知Subtract类

public class Add {private String lock;public Add(String lock) {super();this.lock = lock;}public void add(){synchronized (lock){ValueObject.list.add("anyThings");lock.notifyAll();}}
}

Subtract执行减法操作,进入等待状态,等待Add类唤醒notify

public class Subtract {private String lock;public Subtract(String lock) {super();this.lock = lock;}public void subtract(){try {synchronized (lock){if (ValueObject.list.size()==0){System.out.println(Thread.currentThread().getName()+"线程开始");lock.wait();System.out.println(Thread.currentThread().getName()+"线程结束");}ValueObject.list.remove(0);System.out.println("list长度:"+ValueObject.list.size);}}catch (InterruptedException e){e.printStackTrace();}}
}

线程ThreadAdd

public class ThreadAdd extends Thread {private Add pAdd;public ThreadAdd(Add pAdd) {super();this.pAdd = pAdd;}@Overridepublic void run() {pAdd.add();}
}

线程ThreadSubtract

public class ThreadSubtract extends Thread {private Subtract mSubtract;public ThreadSubtract(Subtract subtract) {super();mSubtract = subtract;}@Overridepublic void run() {mSubtract.subtract();}
}

先开启2个ThreadSubtract,list为空,进入等待状态。在开启1个ThreadAdd线程,向list中添加一个元素,然后唤醒2个ThreadSubtract

public class AddTest {public static void main(String []args) throws InterruptedException{String lock = new String("");Add add = new Add(lock);Subtract subtract = new Subtract(lock);ThreadSubtract subtract1 = new ThreadSubtract(subtract);subtract1.setName("subtract1");subtract1.start();ThreadSubtract subtract2 = new ThreadSubtract(subtract);subtract2.setName("subtract2");subtract2.start();Thread.sleep(1000);ThreadAdd threadAdd = new ThreadAdd(add);threadAdd.setName("threadAdd");threadAdd.start();}
}

第2个ThreadSubtract执行减法操作时,提示下标越界。因为一开始启动了2个ThreadSubtract,等待。ThreadAdd添加1个元素唤醒所有线程,第1个ThreadSubtract接着之前的操作继续执行,删除一个元素并输出集合大小。第2个ThreadSubtract也执行到这的时候,元素已经没有了,就抛异常。
**解决:**从等待状态被唤醒后,重新判断条件,判断是否仍然需要进入等待状态。不需要,才进行下一步。也就是if换成while

public void subtract(){try {synchronized (lock){while (ValueObject.list.size()==0){System.out.println(Thread.currentThread().getName()+"线程开始");lock.wait();System.out.println(Thread.currentThread().getName()+"线程结束");}ValueObject.list.remove(0);System.out.println("list长度:"+ValueObject.list.size);}}catch (InterruptedException e){e.printStackTrace();}}

2、Condition实现等待/通知

关键字synchronized和wait、notify、notifyAll结合,可以实现等待/通知模式。当然ReentrantLock借助Condition对象也可以实现。
condition.await()——>lock.wait()
condition.await(Long time,TimeUnit unit)——>lock.wait(long timeout)
condition.signal()——>lock.notify()
condition.signalAll()——>lock.notifyAll()
synchronized相当于整个ReentrantLock对象只有一个单一的Condition对象,而ReentrantLock却可以有多个Condition对象来通知部分线程。ConditionA.await()进入等待,就有ConditionA.signalAll()通知唤醒。ConditionB.await()进入等待,就有ConditionB.signalAll()通知唤醒。

线程间通信的两种方式相关推荐

  1. 19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  2. 线程间协作的两种方式:wait、notify、notifyAll和Condition

    转载自  线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当 ...

  3. JAVA线程间通信的几种方式

    今天在群里面看到一个很有意思的面试题: "编写两个线程,一个线程打印1~25,另一个线程打印字母A~Z,打印顺序为12A34B56C--5152Z,要求使用线程间的通信." 这是一 ...

  4. python 线程通信的几种方式_进程间通信和线程间通信的几种方式

    进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.在早期面向进程设计的计算机结构中,进程是程序的基本执行实体:在当代 ...

  5. 线程间通信的几种方式

    Java线程间通信: 1:线程上下文 2:共享内存 3:IPC通信 4:套接字(Socket),不同的机器之间进行通信 另外:附注通信内容: linux常用的进程间的通讯方式 (1).管道(pipe) ...

  6. 进程间通信和线程间通信的几种方式

    进程和线程的区别: 对于进程来说,子进程是父进程的复制品,从父进程那里获得父进程的数据空间,堆和栈的复制品. 而线程,相对于进程而言,是一个更加接近于执行体的概念,可以和同进程的其他线程之间直接共享数 ...

  7. java实现线程间通信的四种方式

    synchronized同步 public class MyObject { synchronized public void methodA() { //do something.... } syn ...

  8. java 线程间通信的几种方式

    1.如何让两个线程依次执行 假设有两个线程,一个线程A,一个线程B,两个线程分别依次打印 1-3 三个数字即可. package Test;/** /*** @author Administrator ...

  9. 线程间通信的几种实现方式

    线程间通信的几种实现方式 首先,要短信线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的.我们来基本一道面试常见的题目来分析: 题目:有两个线程A.B,A线程向一个集合里面 ...

最新文章

  1. 编译内核指定模块,筛选当前模块依赖的组件
  2. 项目总监批评程序员穿大裤衩上班情商低!程序员一气之下要离职!项目已到一半了,总监着急发帖求助!...
  3. 牛顿斯科特MATLAB求积分,详解Matlab求积分的各种方法
  4. apidoc文档项目构建
  5. easyUI之Messager(消息窗口)
  6. idea新建maven项目没有src目录的操作方法
  7. 基于深度学习的数字识别GUI的设计
  8. strpos、 strstr、 substr三个函数的对比讲解
  9. 双极性根升余弦信号qpsk_基于CCSDS协议的中频信号源设计与实现
  10. 兆骑科创平台创新创业赛事路演,投融资服务
  11. 用u盘进不了pe计算机意外地,u盘装系统启动不了无法进入pe怎么办
  12. Zookeeper kick off
  13. web服务器和数据库服务器分离的优势
  14. 患病职工解除劳动关系可以吗
  15. JAVA图形界面中的事件处理
  16. 详细解读给数据挖掘新手的6个案例
  17. HW:红队眼中的防守弱点与蓝队应对攻击的常用策略
  18. Web表单的十九个最佳设计实践
  19. 腾讯应届生泄漏薪资被骂,薪资保密不是资本家为了压榨工人才弄出来的条款吗?...
  20. window.print()的确认和取消事件

热门文章

  1. Tomcat下HTTPS双向认证配置以及客户端调用案例
  2. 一个简单的synchronized多线程问题、梳理与思考
  3. codewars-013: Ease the StockBroker
  4. 检查Lync SRV记录是否正常
  5. 使用 /proc 文件系统来访问 Linux 内核的内容
  6. 「JOI 2016 Final」断层
  7. docker--在centos镜像安装mysql
  8. nginx伪静态之try_files和rewrite讲解
  9. Zookeeper系列(十)zookeeper的服务端启动详述
  10. MyEclipse中删除对Struts、hibernate、spring的支持