(三)(1)线程间通信---wait和notify的使用
这篇博客记录线程间通信相关api使用以及理解。
首先第一点,我之前的博客里的线程之间也是通信的,但是他们的通信是建立在访问的是同一个变量上的,相当于是变量、数据层面上的通信,而下面要讲的是线程层面上的通信,这种比前者更加可控。
Wait和notify机制
首先明白为什么会出现这个机制。
目的:举个例子,现在有A,B两个线程,A线程可以不停的改变i的值,B线程再i的值为5时终止。
方法:为了实现这种效果,我们需要在B线程的run方法之中添加while循环,不停的进行检测i值是否为5,为5则抛出异常停止或者使用stop,interrupt等。
问题:检测i的值是否为5这个操作,我们称之为轮询,这里的肯定是很耗时很少的,那么就会执行很多次,但是其中有一些检测是没有必要的,浪费了cpu的资源。
于是乎,就产生了等待/唤醒机制,为了解决cpu资源的浪费,以及让程序更加可控。
先从字面上简单理解一下:当一个线程执行某个操作但是不满足条件时,先让它等候着,直到条件满足了,再将它唤醒。当然唤醒就是说接着执行相应的操作。
wait方法:
让当前线程进行等待,将其加入到预执行队列当中,直到终止或者被唤醒为止,这里的预执行队列就是指处于和其他线程一起竞争获得该锁的状态。并且wait会释放当前的锁,这也就是说没有锁你是不能调用该方法的。
notify:
将处于wait状态,且竞争的锁和调用notify方法的线程持有的锁相同的线程唤醒,这个唤醒是随机的,相当于在预执行队列当中随机唤醒一个线程。不过注意notify唤醒并不会立即唤醒,而是将当前同步代码块之中的代码执行结束之后再去唤醒,相当于不会释放锁。
notifyAll:顾名思义,唤醒依赖于当前锁所有处于wait的线程。
下面通过一个简单的例子来验证上述结论,就是之前的那个例子,A线程列表元素不为5时wait,B线程负责为5时notify:
MyList.java:
package 第三章_wait_join; import java.util.ArrayList;
import java.util.List; public class MyList {
private static List<String> list = new ArrayList<String>();
public static void add(){
list.add("##");
}
public static int getSize(){
return list.size();
}
}
ThreadA.java
package 第三章_wait_join; public class ThreadA extends Thread{
private String lock;
public ThreadA(String lock){
this.lock=lock;
}
@Override
public void run(){
try{
synchronized(lock){
if(MyList.getSize()!=){ //不为5则wait
System.out.println("等待开始...");
lock.wait();
System.out.println("等待结束...");
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
ThreadB.java
package 第三章_wait_join; public class ThreadB extends Thread{
private String lock;
public ThreadB(String lock){
this.lock=lock;
}
@Override
public void run(){
try{
synchronized(lock){
for(int i=;i<;i++){
MyList.add();
if(MyList.getSize()==){
System.out.println("发出通知");
lock.notify();
}
System.out.println("添加了"+(i+)+"个元素");
Thread.sleep();
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
test.java:
package 第三章_wait_join; public class test {
public static void main(String[] args){
try {
ThreadA A = new ThreadA("lock");
ThreadB B = new ThreadB("lock");
A.start();
Thread.sleep();
B.start();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
运行结果:
可以看出来,A线程开始等待之后就释放lock锁,B线程获取到了该锁,执行代码,添加了5个元素时,发出了通知,但是发出通知之后,它没有释放锁,而是将同步代码块执行完,然后再释放锁,A线程获取到,执行wait下面的代码。
说明两点:
1.另外和之前一样,如果一个线程已经处于阻塞状态了,那么就不能再调用其他会产生阻塞的方法,比如调用了wait就不能调用interrupt,suspend,否则会产生异常,你无法阻塞一个已经被阻塞的线程。
2.前面的wait都是没有参数的,wait(long)就是说在long长时间之内,如果没有被唤醒,那么就自动唤醒该线程,很好理解,
通知过早
那么wait,notify肯定也是有一定顺序的,你不能还没有wait就notify,那么是不会notify任何线程的,这也叫做通知过早。看下面的例子:
更改之前的test.java
package 第三章_wait_join; public class test {
public static void main(String[] args){
try {
ThreadA A = new ThreadA("lock");
ThreadB B = new ThreadB("lock");
B.start();
Thread.sleep();
A.start();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
运行结果:
可以看到虽然发出了通知,但是这个等待永远不会结束,因为你在发出通知的时候线程还没有处于阻塞状态,而是处于就绪状态,notify并不会唤醒任何线程。
(三)(1)线程间通信---wait和notify的使用的更多相关文章
- Java线程间通信之wait/notify
Java中的wait/notify/notifyAll可用来实现线程间通信,是Object类的方法,这三个方法都是native方法,是平台相关的,常用来实现生产者/消费者模式.我们来看下相关定义: w ...
- Java 如何实现线程间通信?(notify、join、CountdownLatch、CyclicBarrier、FutureTask、Callable )
转自:https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247486499&idx=1&sn=d3f2d6959df ...
- Java多线程编程(三)线程间通信
线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...
- C++多线程编程(三)线程间通信
多线程编程之三——线程间通讯 作者:韩耀旭 原文地址:http://www.vckbase.com/document/viewdoc/?id=1707 七.线程间通讯 一般而言,应用程序中的一个次要线 ...
- java多线程编程核心技术(三)--线程间通信
1.等待/通知机制 1.wait()方法:使当前执行代码的线程进行等待.wait()方法是Object类的方法,该方法将当前线程放入“预执行队列”中,并在wait()所处的代码行处停止执行.只到被唤起 ...
- Java并发编程:线程间通信wait、notify
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- 线程间通信--wait和notify
使用wait.notify方法实现线程间的通信(注意这两个方法都是object的类的方法,换句话说java为所有的对象都提供了这两个方法) 1.wait和notify必须配合synchronized关 ...
- 线程间通信wait和notify【All】简介
1.通信就是指相互交换一些数据或者发送一些控制指令,比如一个线程给另一个暂停执行的线程发送一个恢复执行的指令. 可变共享变量是天然的通信媒介,也就是说一个线程如果想和另一个线程通信的话,可以修改某个在 ...
- 线程间通信的三种方式(NSThread,GCD,NSOperation)
一.NSThread线程间通信 #import "ViewController.h" @interface ViewController ()<UIScrollViewDel ...
随机推荐
- springboot之mybatisplus,mp的简单理解
这是一张简单的service的继承图.可以看到我们的执行类,即XxxServiceImpl的继承关系. 从上到下,ServiceImpl和BaseMapper是一个依赖关系,ServiceImpl和I ...
- Java编程思想:泛型方法
import java.util.*; public class Test { public static void main(String[] args) { // GenericMethods.t ...
- 在eclipse中利用正则表达式查找替换
众所周知,eclipse是可以用正则表达式来进行查找的,那么怎么利用正则表达式进行替换呢? 方法也很简单,就是在Replace with: 里面输入$来代表捕获型括号的匹配结果,$1为第一个匹配结果, ...
- [HAOI2006]聪明的猴子 题解
题意: 在一个热带雨林中生存着一群猴子,它们以树上的果子为生.昨天下了一场大雨,现在雨过天晴,但整个雨林的地表还是被大水淹没着,部分植物的树冠露在水面上.猴子不会游泳,但跳跃能力比较强,它们仍然可以在 ...
- java反射构建对象和方法的反射调用
Java反射技术应用广泛,其能够配置:类的全限定名,方法和参数,完成对象的初始化,设置是反射某些方法.可以增强java的可配置性. 1.1 通过反射构建对象(无参数): 例如我们使用 ReflectS ...
- Feign挡板和Mock
背景: 在项目开发中,会有调用第三方接口的场景.当开发时,对方不愿意提供测试服务器给我们调用,或者有的接口会按调用次数进行计费.当联调时,第三方的测试服务器也可能会出现不稳定,如果他们的服务挂了,我们 ...
- c语言进阶2-变量的作用域与无参函数
一. 什么是函数 函数是具有特定功能的模块.可以说一个完整的程序其实是由多个函数共同完成的.C语言的全部工作都是由程式各样的函数完成的,所以也把C语言称为函数式语言.使用模块化设计可能 使 ...
- Django REST Framework(DRF)_第二篇
视图和路由 视图封装 第一次封装 上一篇最后我们对书籍表做了增删改查,那么如果现在我们有几十上百张表需要这样做呢?我们知道类的特性有封装,因此我们可以尝试进行封装下. from rest_fram ...
- python课堂整理19----迭代器和生成器
一.概念 • 迭代器协议: 对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么引起一个stopIteration异常,以终止迭代(只能往后走,不能往前退) • 协议是一种约定,pyt ...
- java - 如何使一个类不能被继承
使用final关键字: 使用私有构造器: public final class InitTest{ private InitTest(){} }