java多线程wait_Java多线程 wait, notify 和 notifyAll

Java的Object类

public classObject {public final native voidnotify();public final native voidnotifyAll();public final native void wait(long timeout) throwsInterruptedException;

}

调用这些方法的当前线程必须拥有此对象监视器,否则将会报java.lang.IllegalMonitorStateException exception

wait;

Object的wait方法有三个重载方法,其中一个方法wait() 是无限期(一直)等待,直到其它线程调用notify或notifyAll方法唤醒当前的线程;另外两个方法wait(long timeout) 和wait(long timeout, int nanos)允许传入 当前线程在被唤醒之前需要等待的时间,timeout为毫秒数,nanos为纳秒数。

wait():释放占有的对象锁,线程进入等待池,释放cpu,而其他正在等待的线程即可抢占此锁,获得锁的线程即可运行程序。

而sleep()不同的是,线程调用此方法后,会休眠一段时间,休眠期间,会暂时释放cpu,但并不释放对象锁。也就是说,在休眠期间,其他线程依然无法进入此代码内部。休眠结束,线程重新获得cpu,执行代码。

wait()和sleep()最大的不同在于wait()会释放对象锁,而sleep()不会!

notify:

notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。

调用notify()后,并不会立即释放锁,而是继续执行当前代码,直到synchronized中的代码全部执行完毕,才会释放对象锁。JVM则会在等待的线程中调度一个线程去获得对象锁,执行代码。

需要注意的是,wait()和notify()必须在synchronized代码块中调用。

所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。

notifyAll:

notifyAll 会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。

这些方法可以使用于“生产者-消费者”问题,消费者是在队列中等待对象的线程,生产者是在队列中释放对象并通知其他线程的线程。

demo:

public classMain {public static voidmain(String[] args) {

Message msg= new Message("process it");

Waiter waiter= newWaiter(msg);new Thread(waiter,"waiterThread").start();

Waiter waiter1= newWaiter(msg);new Thread(waiter1, "waiter1Thread").start();

Notifier notifier= newNotifier(msg);new Thread(notifier, "notifierThread").start();

System.out.println("All the threads are started");

}

}classMessage {privateString msg;publicMessage(String str){this.msg=str;

}publicString getMsg() {returnmsg;

}public voidsetMsg(String str) {this.msg=str;

}

}class Waiter implementsRunnable{privateMessage msg;publicWaiter(Message m){this.msg=m;

}

@Overridepublic voidrun() {

String name=Thread.currentThread().getName();synchronized(msg) {try{

System.out.println(name+" waiting to get notified at time:"+System.currentTimeMillis());

msg.wait();

}catch(InterruptedException e){

e.printStackTrace();

}

System.out.println(name+" waiter thread got notified at time:"+System.currentTimeMillis());//process the message now

System.out.println(name+" processed: "+msg.getMsg());

}

}

}class Notifier implementsRunnable {privateMessage msg;publicNotifier(Message msg) {this.msg =msg;

}

@Overridepublic voidrun() {

String name=Thread.currentThread().getName();

System.out.println(name+" started");try{

Thread.sleep(1000);synchronized(msg) {

msg.setMsg(name+" Notifier work done");

msg.notify();//msg.notifyAll();

}

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

All the threads are started

waiter1Thread waiting to get notified at time:1480592377675waiterThread waiting to get notified at time:1480592377677notifierThread started

waiter1Thread waiter thread got notified at time:1480592378685waiter1Thread processed: notifierThread Notifier work done

View Code

Notifier中的run方法改成msg.notifyAll();

Result:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

All the threads are started

waiter1Thread waiting to get notified at time:1480592533780waiterThread waiting to get notified at time:1480592533780notifierThread started

waiterThread waiter thread got notified at time:1480592534782waiterThread processed: notifierThread Notifier work done

waiter1Thread waiter thread got notified at time:1480592534782waiter1Thread processed: notifierThread Notifier work done

View Code

一个通知线程,3个等待线程:

public classMain {private String flag[] = { "true"};public static voidmain(String[] args) {

System.out.println("Main Thread Run!");

Main test= newMain();

System.nanoTime();

NotifyThread notifyThread=test.new NotifyThread("notify01");

WaitThread waitThread01= test.new WaitThread("waiter01");

WaitThread waitThread02= test.new WaitThread("waiter02");

WaitThread waitThread03= test.new WaitThread("waiter03");

notifyThread.start();

waitThread01.start();

waitThread02.start();

waitThread03.start();

}class NotifyThread extendsThread{publicNotifyThread(String name) {super(name);

}public voidrun() {try{

sleep(3000);//推迟3秒钟通知

} catch(InterruptedException e) {

e.printStackTrace();

}synchronized(flag) {

flag[0] = "false";

flag.notifyAll();

}

}

}class WaitThread extendsThread {publicWaitThread(String name) {super(name);

}public voidrun() {synchronized(flag) {while (flag[0] != "false") {

System.out.println(getName()+ " begin waiting!");long waitTime =System.currentTimeMillis();try{

flag.wait();

}catch(InterruptedException e) {

e.printStackTrace();

}

waitTime= System.currentTimeMillis() -waitTime;

System.out.println("wait time :" +waitTime);

}

System.out.println(getName()+ " end waiting!");

}

}

}

}

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

Main Thread Run!waiter01 begin waiting!waiter02 begin waiting!waiter03 begin waiting!wait time :2999waiter03 end waiting!wait time :2999waiter02 end waiting!wait time :2999waiter01 end waiting!

View Code

=====================================================================================

wait和sleep的demo:

public classMain {public static voidmain(String[] args) {new Thread(newThread1()).start();try{

Thread.sleep(5000);

}catch(Exception e) {

e.printStackTrace();

}new Thread(newThread2()).start();

}private static class Thread1 implementsRunnable{

@Overridepublic voidrun(){synchronized (Main.class) {

System.out.println("enter thread1...");

System.out.println("thread1 is waiting...");try{//调用wait()方法,线程会放弃对象锁,进入等待此对象的等待锁定池

Main.class.wait();

}catch(Exception e) {

e.printStackTrace();

}

System.out.println("thread1 is going on ....");

System.out.println("thread1 is over!!!");

}

}

}private static class Thread2 implementsRunnable{

@Overridepublic voidrun(){synchronized (Main.class) {

System.out.println("enter thread2....");

System.out.println("thread2 is sleep....");//只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

Main.class.notify();//==================//区别//如果我们把代码:Main.class.notify();给注释掉,即Main.class调用了wait()方法,但是没有调用notify()//方法,则线程永远处于挂起状态。

try{//sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,//但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。//在调用sleep()方法的过程中,线程不会释放对象锁。

Thread.sleep(5000);

}catch(Exception e) {

e.printStackTrace();

}

System.out.println("thread2 is going on....");

System.out.println("thread2 is over!!!");

}

}

}

}

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

enter thread1...

thread1 is waiting...

enter thread2....

thread2 is sleep....

thread2 is going on....

thread2 is over!!!thread1 is going on ....

thread1 is over!!!

View Code

wait,notify的使用,线程同步唤醒

package com.qhong;/**

* Created by Administrator on 2017/7/4 0004.*/

public classMyThreadPrinter2 implements Runnable {privateString name;privateObject prev;privateObject self;privateMyThreadPrinter2(String name, Object prev, Object self) {this.name =name;this.prev =prev;this.self =self;

}

@Overridepublic voidrun() {int count = 10;while (count > 0) {

synchronized (prev) {

synchronized (self) {

System.out.print(name);

count--;

self.notify();

}try{

prev.wait();

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

}public static voidmain(String[] args) throws Exception {

Object a= newObject();

Object b= newObject();

Object c= newObject();

MyThreadPrinter2 pa= new MyThreadPrinter2("A", c, a);

MyThreadPrinter2 pb= new MyThreadPrinter2("B", a, b);

MyThreadPrinter2 pc= new MyThreadPrinter2("C", b, c);newThread(pa).start();

Thread.sleep(1000); //确保按顺序A、B、C执行

newThread(pb).start();

Thread.sleep(1000);newThread(pc).start();

Thread.sleep(1000);

}

}

Output:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

ABCABCABCABCABCABCABCABCABCABC

View Code

Thread.sleep(100)是保证ABC的执行顺序

在这个例子中,可以深刻的体现出wait不仅是阻塞线程,更体现wait释放锁对象。

571e0815ebda59332a2edf152fbe6098.png

比如是4轮,第一轮的abc是有Thread.sleep来控制的,这个时候只有锁对象c是被释放的,锁对象a阻塞线程B,锁对象b阻塞线程C

第二轮,a的prev是c,所以是可以运行的,并且在这一步释放锁对象a,锁对象c注释线程A

................

就这样一轮一轮的控制,虽然想通了,但是让我写还是想不出来的,挺繁琐!

有是一个demo

package com.qhong.thread;public classMyWaitNotify{

MonitorObject myMonitorObject= newMonitorObject();

boolean wasSignalled= false;public voiddoWait(){

synchronized(myMonitorObject){while(!wasSignalled){try{

myMonitorObject.wait();

}catch(InterruptedException e){...}

}//clear signal and continue running.

wasSignalled = false;

}

}public voiddoNotify(){

synchronized(myMonitorObject){

wasSignalled= true;

myMonitorObject.notify();

}

}

}class MonitorObject{}

代码比较简单,但是要注意下面的说明,有好几个概念以前没注意:

1、不管是等待线程还是唤醒线程都在同步块里调用wait()和notify()。这是强制性的!一个线程如果没有持有对象锁,将不能调用wait(),notify()或者notifyAll()。否则,会抛出IllegalMonitorStateException异常。

2、一旦线程调用了wait()方法,它就释放了所持有的监视器对象上的锁。这将允许其他线程也可以调用wait()或者notify()。

3、为了避免丢失信号,必须把它们保存在信号类里。如上面的wasSignalled变量。

4、假唤醒:由于莫名其妙的原因,线程有可能在没有调用过notify()和notifyAll()的情况下醒来。这就是所谓的假唤醒(spurious wakeups)。为了防止假唤醒,保存信号的成员变量将在一个while循环里接受检查,而不是在if表达式里。这样的一个while循环叫做自旋锁。

5、不要在字符串常量或全局对象中调用wait()。即上面MonitorObject不能是字符串常量或是全局对象。每一个MyWaitNotify的实例都拥有一个属于自己的监视器对象,而不是在空字符串上调用wait()/notify()。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值