前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 同步方式 (2) —— wait和notify/notifyall

Java 同步方式 (2) —— wait和notify/notifyall

作者头像
阳光岛主
发布2019-02-19 16:05:33
4170
发布2019-02-19 16:05:33
举报
文章被收录于专栏:米扑专栏米扑专栏

Java 中除了关键字 synchronized 能够实现线程同步外,还可以使用 wait 和 notify/notify 实现同步。

wait 方法是使拥有当前对象(object)的线程(thread)放弃锁(release lock),进入睡眠状态

notify 通知该对象(object)因上面调用wait而等待的某一进程重新唤醒启动

notifyAll 通知在对象(object)上因调用wait而等待的所有进程启动,这些进程根据优先级顺序执行

一个线程在其生命周期内总是处于某种状态:

  1. 创建: 当一个线程对象被声明并创建后,它处于“创建”状态;
  2. 就绪:线程对象调用 start() 方法后,将进入“就绪”状态,处于“就绪”状态的线程不是立即执行,而是进入就绪队列,等待CPU;
  3. 运行:当就绪队列中具有最高优先级的就绪线程被调度并获得CPU时,便进入“运行”状态,执行 run() 方法,run 方法中定义了线程的操作和功能;
  4. 非运行:处于“运行”状态的线程可能因为某些原因 (例如人为挂起)进入“非运行”状态,让出CPU并临时中止自己的执行;
  5. 停止:线程完成了它的全部工作或调用 stop() 方法强制中止线程,线程就进入“停止”状态。

wait 与 sleep 区别

wait

sleep

归属类

属于Object类(java.lang.Object)

属于Thread类(java.lang.Thread),静态方法

释放锁

释放了锁,其它线程同步块或方法

没有释放锁,不出让系统资源(如cpu)

中断唤醒

wait一般不会加时间限制,而是判断是否满足符合条件;如果符合条件,则notify/notifyall唤醒

sleep(milliseconds)后自动唤醒,如果时间不到可用interrupt()强制中断

适用范围

同步方法或同步块使用(synchronized)

任何地方都可使用(main、thread线程)

捕获异常

必须捕获异常(try/catch)

不需要捕获异常

wait - sleep 示例(区别)

代码语言:javascript
复制
package com.homer.thread;

public class waitsleep {
	public static void main(String[] args) {
		ThreadDemo th = new ThreadDemo();
		
		th.start();
		System.out.println("thread is starting...");
		
		synchronized (th) {
			try {
				System.out.println("Waiting for th to complete...");
//				th.wait(1000);		// 等待1秒后,立刻执行
				th.wait();			// 线程等待,notify唤醒后执行
			} catch (Exception e) {
				e.printStackTrace();
			}
			System.out.println("Total is : " + th.total);	// 线程唤醒后,执行
		}
	}
}

class ThreadDemo extends Thread {
	int total;
	
	@Override
	public void run(){
		try {
			Thread.sleep(2000);		// 睡眠2秒
			synchronized(this){
				System.out.println("Thread is running...");
				for(int i=0; i<10; i++) {
					total += i;
					System.out.println("i = " + i + "; total = " + total);
					
					if(i==5) {
						System.out.println("i = 5 sleep(3000)");
						Thread.sleep(3000);		// i = 5时,睡眠3秒
					}
				}
				this.notify();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

运行结果:

thread is starting... Waiting for th to complete... Thread is running... i = 0; total = 0 i = 1; total = 1 i = 2; total = 3 i = 3; total = 6 i = 4; total = 10 i = 5; total = 15 i = 5 sleep(3000) i = 6; total = 21 i = 7; total = 28 i = 8; total = 36 i = 9; total = 45 Total is : 45

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

wait - notify 示例(生产者 - 消费者)

代码语言:javascript
复制
package com.homer.thread;

public class waitnotify {
	public static void main(String[] args) {
		Q q = new Q();
		new Producer(q);
		new Consumer(q);
	}
}

class Producer implements Runnable {
	Q q = null;
	
	public Producer(Q q) {
		this.q = q;
		(new Thread(this, "Producer")).start();
	}
	
	@Override
	public void run() {
		int i = 0;
		while(i<5) {
			q.put(i++);
		}
	}
}

class Consumer implements Runnable {
	Q q = null;
	
	public Consumer(Q q) {
		this.q = q;
		(new Thread(this, "Consumer")).start();
	}

	@Override
	public void run() {
		while(q.get()<5){
		}
	}
}

class Q {
	int n;
	boolean valueSet = false;
	
	public synchronized int get() {
		if(!valueSet) {		// if valueSet == false,wait else try to got value
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} 
		
		System.out.println("Get n : " + n);
		valueSet = false;
		notify();
		
		return n;
	}
	
	public synchronized void put(int n) {
		if(valueSet) {		// if valueSet == true,already have value so wait fetch,else put 
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		this.n = n;
		System.out.println("Put n : " + n);
		valueSet = true;
		notify();
	}
}

运行结果:

Put n : 0 Get n : 0 Put n : 1 Get n : 1 Put n : 2 Get n : 2 Put n : 3 Get n : 3 Put n : 4 Get n : 4

首先两个线程启动,他们的执行占用CPU多少随机,但是这里因为加了一个锁的Boolean型变量,而控制了put与set. 

首先:创建了一个对象Q,创建了一个Producer,一个Consumer,这两个对象在构造方法中启动了线程.  第一步:     对于Producer来说,会首先去调用put方法,因为valueSet是默认值是false,所以在Q的put方法不执行wait 而是执行 this.n = n 赋值操作,执行完毕后设置为valueSet = true     对于Consumer来说,会首先去调用get方法,因为valueSet是默认值是false,所以该线程会执行wait(等待valueSet 赋值状态为true) 第二步:    对于Producer来说,因为valueSet已经变成true,所以会wat.     对于Consumer来说,因为valueSet已经变成true,所以会执行下面的code(get value),然后设置valueSet为false. 第三步:    Producer执行put方法,因为valueSet为false    Consumer等待(重复第一步) 依次类推,方法执行...  这里关键是加了一个共享的变量 valueSet 来判是该取值get,还是put值。当然有了wait跟notify才使它成为了可以实现的。  但是不管怎样,wait是使目前控制该对象(Q的对象q)的线程wait(等待),notify是使前面在该对象上面wait的方法继续执行. 

示例代码下载

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2012年06月23日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档