java 多线程 线程之间的协作 notify()和notifyAll()

    使用notify()而不是notifyAll()是一种优化。使用notify()时,在众多等待同一个锁的任务中只有一个会被唤醒,因此如果你希望使用notify(),就必须保证被唤醒的是恰当的任务。另外,为了使用notify(),所有任务必须等待相同的条件,因为如果你有多个任务在等待不同的条件,那么你就不会知道是否唤醒了恰当的任务。如果使用notify(),当条件发生变化时,必须只有一个任务能够从中受益。最后,这些限制对所有可能存在的子类都必须总是起作用。如果这些规则中有任何一条不满足,那么你就必须使用notifyAll()而不是notify()。

   

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 在有关Java的线程机制的讨论中,有一个令人困惑的描述:notify()将唤醒“所有正在等待的任务”。这是否意味着在程序
 * 中任何地方,任何处于wait()状态中的任务都将被任何对notifyAll()的调用唤醒呢?在下面的示例中,与Task2相关的
 * 代码说明了情况并非如此---事实上,当notifyAll()因某个特定锁而被调用时,只有等待这个锁的任务才会被唤醒。
 * 
 * 
 * @create @author Henry @date 2016-12-21
 * 
 */

class Blocker {
	private String name;

	public Blocker(String name) {
		this.name = name;
	}

	synchronized void waitingCall() {
		try {
			while (!Thread.interrupted()) {
				wait();
				System.out.println(Thread.currentThread() + " " + name);
			}
		} catch (InterruptedException e) {
			// OK to exit this way
			System.out.println("InterruptedException");
		}
	}

	synchronized void prod() {
		notify();
	}

	synchronized void prodAll() {
		notifyAll();
	}
}
/**
 * Task 有其自己的Blocker对象。
 * 
 * @create @author Henry @date 2016-12-21
 * 
 */
class Task implements Runnable {
	static Blocker blocker = new Blocker("Task");

	@Override
	public void run() {
		blocker.waitingCall();
	}
}
/**
 * Task2 有其自己的Blocker对象。
 * 
 * @create @author Henry @date 2016-12-21
 * 
 */
class Task2 implements Runnable {
	// A separate Blocker object;
	static Blocker blocker = new Blocker("Task2");

	@Override
	public void run() {
		blocker.waitingCall();
	}
}
/**
 * Task和Task2每个都有其自己的Blocker对象,因此每个Task对象都会在Task.blocker上阻塞,而每个Task2都会在
 * Taks2.blocker上阻塞。在main()中,java.util.Timer对象呗设置为每4/10秒执行一次run()方法,而这个run()
 * 方法将经由“激励”方法交替地在Task.blocker上调用notify()和notifyAll()。
 * 从输出中你可以看到,即使存在Task2.blocker上阻塞的Task2对象,也没有任何在Task.blocker上的notify()或
 * notifyAll()调用会导致Task2对象被唤醒。于此类似,在main()的结尾,调用了timer的cancel(),即使计时器被撤销了,
 * 前5个任务也依然在运行,并仍旧在它们对Task.blocker.waitingCall()的调用中被阻塞。对Task2.blocker.prodAll()
 * 的调用所产生的输出不包括任何在Task.blocker中的锁上等待任务。
 * 如果你浏览Blocker中的prod()和prodAll(),就会发现这是有意义的。这些方法是synchronized的,这意味着它们将获取自身
 * 的锁,因此当它们调用notify()或notifyAll()时,只在这个锁上调用是符合逻辑的---因此,将只唤醒在等待这个特定锁的任务。
 * Blocker.waitingCall()非常简单,以至于在本例中,你只需声明for(;;)而不是while(!Thread.interrupted())就可以到达
 * 相同的效果,因为在本例中,由于异常而离开循环和通过检查interrupted()标志离开循环时没有任何区别的---在两种情况下都要
 * 执行相同的代码。但是事实上,这个示例选择检查interrupted(),因为存在着两种离开循环的方式。如果在以后的某个时刻,
 * 你决定要循环中添加更多的代码,那么如果没有覆盖从这个循环中退出的这两条路径,就会产生引入错误的风险。
 * 
 * @create @author Henry @date 2016-12-21
 * 
 */
public class NotifyVsNotifyAll {
	public static void main(String[] args) throws Exception {
		ExecutorService exec = Executors.newCachedThreadPool();
		for (int i = 0; i < 5; i++)
			exec.execute(new Task());
		exec.execute(new Task2());
		Timer timer = new Timer();
		timer.scheduleAtFixedRate(new TimerTask() {
			boolean prod = true;

			@Override
			public void run() {
				if (prod) {
					System.out.println("\nnotify()");
					Task.blocker.prod();
					prod = false;
				} else {
					System.out.println("\nnotifyAll()");
					Task.blocker.prodAll();
					prod = true;
				}
			}
		}, 400, 400);// Run every .4 second
		TimeUnit.SECONDS.sleep(5);
		timer.cancel();
		System.out.println("\n Timer canceled");

		TimeUnit.MILLISECONDS.sleep(500);
		System.out.println("Task2.blocker.prodAll()");
		Task2.blocker.prodAll();

		TimeUnit.MILLISECONDS.sleep(500);
		System.out.println("\n Shutting down");
		exec.shutdownNow();
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值