【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进行介绍,然后深入了解一下它的源码,最后通过示例来掌握它的 ...
随机推荐
- coreseek安装过程
一.sphinx 全文检索 通过sphinx检索到id,然后到mysql里面拿到记录 什么是劝我呢检索?结构化数据: 具有固定格式或者长度的数据非结构化数据: 标题 内容 等不定长的数据非机构化数据还 ...
- MySql 小记
MySql 简单 小记 以备查看 1.sql概述 1.什么是sql? 2.sql发展过程? 3.sql标准与方言的关系? 4.常用数据库? 5.MySql数据库安装? 2.关键概念 表结构----- ...
- 内网穿透神器(ngrok)服务端部署【分享一台自己的ngrok服务器】【多平台】
Ngrok为何物 “ngrok 是一个反向代理,通过在公共的端点和本地运行的 Web 服务器之间建立一个安全的通道.ngrok 可捕获和分析所有通道上的流量,便于后期分析和重放.”这是百度百科上给Ng ...
- 引入DecimalFormat类进行数字格式化操作
引入语句:import java.text.DecimalFormat; 首先创建DecimalFormat类对象,利用类对象调用Format()方法进行格式化操作.这里有两种方法:①.Decimal ...
- Ubuntu Server 12.04下部署glusterfs
1.安装环境 Linux:Ubuntuserver 12.04.1 LTS 64bit 2台 分布式文件系统:Gluster 测试环境:一台作文件服务器端(192.168.56.133),一台作客户端 ...
- ASP.NET是否存在客户端控件?
在很久很久以前,传说...在ASP.NET开发中,使用服务端控件,比客户端控件更加损耗性能... 那么ASP.NET中是否有客户端控件呢? 有些人就说了,打开VS,然后在左边,工具箱里,有个HTML部 ...
- label和input里面文字不对齐的解决方法!
测试了集中方法,发现不行.只能用专署标签解决这个问题. <fieldset> <legend>神光咨询后台管理登录</legend> <br /& ...
- UESTC 1546 Bracket Sequence
Bracket Sequence Time Limit: 3000MS Memory Limit: 65536KB 64 ...
- Spring配置文件集成Hibernate配置文件
Spring对hibernate配置文件hibernate.cfg.xml的集成,来取代hibernate.cfg.xml的配置. spring对hibernate配置文件hibernate.c ...
- cookie (储存在用户本地终端上的数据)
Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份.进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密).定义于 RFC2109 和 2965 中的都已废弃 ...