【Java并发系列02】Object的wait()、notify()、notifyAll()方法使用
一、前言
对于并发编程而言,除了Thread以外,对Object对象的wati和notify对象也应该深入了解其用法,虽然知识点不多。
二、线程安全基本知识
首先应该记住以下基本点,先背下来也无妨:
- 同一时间一个锁只能被一个线程持有
- 调用对象的wait()和notify()前必须持有它
三、wait()和notify()理解
3.1 wait()和notify()方法简介
wait()和notify()都是Object的方法,可以认为任意一个Object都是一种资源(或者资源的一个代表),当多个线程对一个资源进行操作时,如果线程发现这个资源还没有准备好,它就可以在这个资源上进行等待,即调用这个资源的wait()方法,如果有另外的线程经过某些处理觉得这个资源可用了,会调用这个这个资源的notify()方法,告诉等待它的线程,这个资源可以用了。
当然不使用wait()和notify()方法也是可以的,可以用while()死循环来判断,如下面的伪代码:
class Resource{
static boolean canUse=false;
} while(!Resource.canUse){
//如果不可用,死循环在这里等待
} //当资源可以使用后,就会跳出循环,往下执行
这样做是可以,但是特别消耗CPU资源,所以建议用户使用wait()和notify()方法。
3.2 wait()和notify()的价值
其实从单词意思来看就能看出来,wait就是等待,说明这个资源没有准备好,我要等,还有这一个wait(long timeout) ,表示我只能等待你这么长时间了,过时不候啊,而调用notify()的线程肯定就是对资源进行处理的,处理完进行通知。所以呢,它们就经常用在生产者和消费者模式中。任何涉及等资源到来的情景都适合用这两个方法,
3.3 为什么wait()和notify()必须和synchronized一起使用
当不在synchronized同步块中使用wait()和notify()或者调用方法的对象不是synchronized的同步锁就会抛异常:
java.lang.IllegalMonitorStateException
很多人会疑惑为什么必须持有这个对象的锁才能调用对象的wait()和notify()方法呢,我也有这个疑惑,而且我认为这么做是没有必要的。首先看下面的代码:
public class WaitTest{
// 这是一个资源,模拟的Object
final NoObjct resource=new NoObjct();
public static void main(String[] args) throws Exception{
WaitTest d=new WaitTest();
d.test();
} public void test() throws Exception{
Runnable r=new Runnable(){
public void run(){
// 调用资源的模拟的wait方法,在方法内部使用synchronized
resource.noWait();
System.out.println("线程等待完,执行");
}
};
Thread t=new Thread(r);
t.start();
Thread.sleep(2000);
System.out.println("准备唤醒等待资源的线程");
// 调用资源的模拟的notify方法,在方法内部使用synchronized
resource.noNotify();
}
} // 因wait()和notify()是final方法,不能覆盖,所以模拟一个Object对象
class NoObjct{
// 模拟wait方法
public void noWait(){
// 这个就相当于将synchronized放到wait方法内部
synchronized(this){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
// 模拟notify方法
public void noNotify(){
// 这个就相当于将synchronized放到notify方法内部
synchronized(this){
this.notify();
}
}
}
这是一个简单的wait()和notify()例子,wait等待,notify唤醒。如果忽略掉模拟的Object会发现代码简洁了许多,否则就要每次使用synchronized,如下代码:
public class WaitTest{
// 这是一个资源,模拟的Object
final Object resource=new Object(); public static void main(String[] args) throws Exception{
WaitTest d=new WaitTest();
d.test();
} public void test() throws Exception{
Runnable r=new Runnable(){
public void run(){
// 必须使用synchronized
try{
synchronized(resource){
resource.wait();
}
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("线程等待完,执行");
}
};
Thread t=new Thread(r);
t.start(); Thread.sleep(2000);
System.out.println("准备唤醒等待资源的线程");
// 必须使用synchronized
synchronized(resource){
resource.notify();
}
}
}
所以呢,我觉得wait()和notify()和synchronized一起没有什么意义,毕竟synchronized用来进行代码同步的,和线程之间唤醒没有什么关系(希望有读者能给我相反的意见并说服我)。但是既然这么规定了就必须要去遵守,即必须在synchronized中使用wait和notify,且调用方法的对象必须是同步对象。
四、何时使用wait()和notify()
在上面已经说了这两个方法的其中一个价值就是用在生产者和消费者模式。但是通过使用它们来构建的生产者和消费者模型很低级而且复杂,完全可以使用BlockingQueue接口的实现类来构建。比如可以使用ArrayBlockingQueue,它既能保证线程安全又能实现阻塞效果,何乐而不为呢。
除此之外就只有线程间休眠与唤醒了。
这一篇看似和并发没什么关系,但是因为涉及到多线程还是捎带着讲了一下。
未经许可禁止转载。
【Java并发系列02】Object的wait()、notify()、notifyAll()方法使用的更多相关文章
- 【Java并发编程】:使用wait/notify/notifyAll实现线程间通信
在java中,可以通过配合调用Object对象的wait()方法和notify()方法或notifyAll()方法来实现线程间的通信.在线程中调用wait()方法,将阻塞等待其他线程的通知(其他线程调 ...
- java并发系列(二)-----线程之间的协作(wait、notify、join、CountDownLatch、CyclicBarrier)
在java中,线程之间的切换是由操作系统说了算的,操作系统会给每个线程分配一个时间片,在时间片到期之后,线程让出cpu资源,由其他线程一起抢夺,那么如果开发想自己去在一定程度上(因为没办法100%控制 ...
- Java 集合系列 02 Collection架构
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java并发系列[5]----ReentrantLock源码分析
在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可 ...
- Java 并发系列之二:java 并发机制的底层实现原理
1. 处理器实现原子操作 2. volatile /** 补充: 主要作用:内存可见性,是变量在多个线程中可见,修饰变量,解决一写多读的问题. 轻量级的synchronized,不会造成阻塞.性能比s ...
- Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析
学习Java并发编程不得不去了解一下java.util.concurrent这个包,这个包下面有许多我们经常用到的并发工具类,例如:ReentrantLock, CountDownLatch, Cyc ...
- Java并发系列[2]----AbstractQueuedSynchronizer源码分析之独占模式
在上一篇<Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析>中我们介绍了AbstractQueuedSynchronizer基本的一些概 ...
- Java并发系列[3]----AbstractQueuedSynchronizer源码分析之共享模式
通过上一篇的分析,我们知道了独占模式获取锁有三种方式,分别是不响应线程中断获取,响应线程中断获取,设置超时时间获取.在共享模式下获取锁的方式也是这三种,而且基本上都是大同小异,我们搞清楚了一种就能很快 ...
- java io系列02之 ByteArrayInputStream的简介,源码分析和示例(包括InputStream)
我们以ByteArrayInputStream,拉开对字节类型的“输入流”的学习序幕.本章,我们会先对ByteArrayInputStream进行介绍,然后深入了解一下它的源码,最后通过示例来掌握它的 ...
随机推荐
- checkbox全选与非全选之间的切换
<div id="congras_area"> <input type="checkbox" name="" id=&qu ...
- 19个必须知道的Visual Studio快捷键
项目相关的快捷键 Ctrl + Shift + B = 生成项目 Ctrl + Alt + L = 显示Solution Explorer(解决方案资源管理器) Shift + Alt+ C = 添加 ...
- 【原创】贴个dirtycow(脏牛漏洞)不死机的exploit
dirtycow官网上几个获得rootshell的exp大都会导致机器死机,在原作者的基础上改进了一下,做个记录: /* * (un)comment correct payload first (x8 ...
- Excel—使用条件格式标注今日值班者
如下图所示值班表: Step1:选中A2:G2,调出条件格式,在条件格式中,使用公式确定单元格. Step2: 在公式栏中填入以下公式: =TEXT(TODAY(),"aaaa") ...
- jpeg huffman coding table
亮度DC系数的取值范围及序号: 序号(size) 取值范围 0 0 1 - ...
- linux -目录结构
摘自:http://www.comptechdoc.org/os/linux/usersguide/linux_ugfilestruct.html 这个目录结构介绍是我目前看到介绍最全的,有时间在翻译 ...
- linux 命令 之判断表达式
摘自http://www.comptechdoc.org/os/linux/usersguide/linux_ugshellpro.html Tests There is a function pro ...
- spring拦截器排除 静态资源
拦截器需要排除静态资源,不然会造成资源浪费 <!-- 拦截器 --> <mvc:interceptors> <!-- 使用bean定义一个Interceptor,直接定义 ...
- android:clipChildren属性的作用
该属性默认为true,这个属性需要添加到最顶层的ViewGroup,作用是控制子View是否可以超出它所在的父View设定的边界 比如ImageView设置高度100dp,而它所在的父View设置的高 ...
- [mark] 使用Sublime Text 2时如何将Tab配置为4个空格
在Mac OS X系统下,Sublime Text是一款比较赞的编辑器. 作为空格党的自觉,今天mark一下使用Sublime Text 2时如何将Tab配置为4个空格: 方法来自以下两个链接: ht ...