[英]A simple scenario using wait() and notify() in java
我可以得到一个完整的简单方案,即建议如何使用它的教程,特别是在队列中吗?
The wait()
and notify()
methods are designed to provide a mechanism to allow a thread to block until a specific condition is met. wait()
和notify()
方法旨在提供一种机制,允许线程阻塞直到满足特定条件为止。 For this I assume you're wanting to write a blocking queue implementation, where you have some fixed size backing-store of elements. 为此,我假设您要编写一个阻塞队列实现,其中具有一些固定大小的元素后备存储。
The first thing you have to do is to identify the conditions that you want the methods to wait for. 您要做的第一件事是确定您希望方法等待的条件。 In this case, you will want the
put()
method to block until there is free space in the store, and you will want the take()
method to block until there is some element to return. 在这种情况下,您将希望在存储中没有可用空间之前阻塞
put()
方法,并且希望在有一些元素返回之前阻塞take()
方法。
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public synchronized void put(T element) throws InterruptedException {
while(queue.size() == capacity) {
wait();
}
queue.add(element);
notify(); // notifyAll() for multiple producer/consumer threads
}
public synchronized T take() throws InterruptedException {
while(queue.isEmpty()) {
wait();
}
T item = queue.remove();
notify(); // notifyAll() for multiple producer/consumer threads
return item;
}
}
There are a few things to note about the way in which you must use the wait and notify mechanisms. 关于必须使用等待和通知机制的方式,需要注意一些事项。
Firstly, you need to ensure that any calls to wait()
or notify()
are within a synchronized region of code (with the wait()
and notify()
calls being synchronized on the same object). 首先,您需要确保对
wait()
或notify()
任何调用都在代码的同步区域内(其中wait()
和notify()
调用在同一对象上同步)。 The reason for this (other than the standard thread safety concerns) is due to something known as a missed signal. 造成这种情况的原因(除了标准线程安全问题之外)是由于某种原因导致的信号丢失。
An example of this, is that a thread may call put()
when the queue happens to be full, it then checks the condition, sees that the queue is full, however before it can block another thread is scheduled. 这样的一个示例是,当队列碰巧已满时,线程可以调用
put()
,然后检查条件,查看队列是否已满,但是在可以阻止另一个线程调度之前。 This second thread then take()
's an element from the queue, and notifies the waiting threads that the queue is no longer full. 然后,第二个线程
take()
是队列中的一个元素,并通知等待线程该队列不再满。 Because the first thread has already checked the condition however, it will simply call wait()
after being re-scheduled, even though it could make progress. 因为第一个线程已经检查了条件,所以即使重新安排了进度,它也只会在重新调度之后简单地调用
wait()
。
By synchronizing on a shared object, you can ensure that this problem does not occur, as the second thread's take()
call will not be able to make progress until the first thread has actually blocked. 通过在共享库上进行同步,可以确保不会发生此问题,因为第二个线程的
take()
调用直到第一个线程实际被阻塞后才能取得进展。
Secondly, you need to put the condition you are checking in a while loop, rather than an if statement, due to a problem known as spurious wake-ups. 其次,由于被称为虚假唤醒的问题,您需要将要检查的条件放入while循环中,而不是if语句中。 This is where a waiting thread can sometimes be re-activated without
notify()
being called. 在这里有时可以在不调用
notify()
情况下重新激活等待线程。 Putting this check in a while loop will ensure that if a spurious wake-up occurs, the condition will be re-checked, and the thread will call wait()
again. 将此检查放在while循环中将确保如果发生虚假唤醒,将重新检查条件,并且线程将再次调用
wait()
。
As some of the other answers have mentioned, Java 1.5 introduced a new concurrency library (in the java.util.concurrent
package) which was designed to provide a higher level abstraction over the wait/notify mechanism. 正如其他答案中提到的那样,Java 1.5引入了一个新的并发库(位于
java.util.concurrent
包中),该库旨在在等待/通知机制上提供更高级别的抽象。 Using these new features, you could rewrite the original example like so: 使用这些新功能,您可以像这样重写原始示例:
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public void put(T element) throws InterruptedException {
lock.lock();
try {
while(queue.size() == capacity) {
notFull.await();
}
queue.add(element);
notEmpty.signal();
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
while(queue.isEmpty()) {
notEmpty.await();
}
T item = queue.remove();
notFull.signal();
return item;
} finally {
lock.unlock();
}
}
}
Of course if you actually need a blocking queue, then you should use an implementation of the BlockingQueue interface. 当然,如果您实际上需要阻塞队列,则应该使用BlockingQueue接口的实现。
Also, for stuff like this I'd highly recommend Java Concurrency in Practice , as it covers everything you could want to know about concurrency related problems and solutions. 另外,对于这种事情,我强烈建议在实践中使用Java Concurrency ,因为它涵盖了您可能想知道的与并发相关的问题和解决方案的所有内容。
Not a queue example, but extremely simple :) 不是队列示例,但是非常简单:)
class MyHouse {
private boolean pizzaArrived = false;
public void eatPizza(){
synchronized(this){
while(!pizzaArrived){
wait();
}
}
System.out.println("yumyum..");
}
public void pizzaGuy(){
synchronized(this){
this.pizzaArrived = true;
notifyAll();
}
}
}
Some important points: 一些要点:
1) NEVER do 1)永远不要
if(!pizzaArrived){
wait();
}
Always use while(condition), because 始终使用while(condition),因为
while(!pizzaExists){ wait(); }
. while(!pizzaExists){ wait(); }
。 2) You must hold the lock (synchronized) before invoking wait/nofity. 2)在调用等待/无效之前,您必须保持锁(同步)。 Threads also have to acquire lock before waking.
线程也必须在唤醒之前获取锁。
3) Try to avoid acquiring any lock within your synchronized block and strive to not invoke alien methods (methods you don't know for sure what they are doing). 3)尝试避免在同步块内获取任何锁,并努力不调用外来方法(您不确定它们在做什么的方法)。 If you have to, make sure to take measures to avoid deadlocks.
如果需要,请确保采取措施避免死锁。
4) Be careful with notify(). 4)注意notify()。 Stick with notifyAll() until you know what you are doing.
坚持使用notifyAll(),直到您知道自己在做什么。
5)Last, but not least, read Java Concurrency in Practice ! 5)最后但并非最不重要的一点,请阅读实践Java并发 !
Even though you asked for wait()
and notify()
specifically, I feel that this quote is still important enough: 即使您具体要求了
wait()
和notify()
,我仍然认为这句话很重要:
Josh Bloch, Effective Java 2nd Edition , Item 69: Prefer concurrency utilities to wait
and notify
(emphasis his): Josh Bloch, Effective Java 2nd Edition ,项目69:建议并发实用程序
wait
并notify
(强调他的):
Given the difficulty of using
wait
andnotify
correctly, you should use the higher-level concurrency utilities instead [...] usingwait
andnotify
directly is like programming in "concurrency assembly language", as compared to the higher-level language provided byjava.util.concurrent
.考虑到正确使用
wait
和notify
的困难,您应该使用更高级别的并发实用程序,而不是直接使用wait
和notify
,就像使用“并发汇编语言”进行编程,相比之下,java.util.concurrent
。 There is seldom, if ever, reason to usewait
andnotify
in new code .几乎没有理由使用
wait
并在新代码中进行notify
。
Have you taken a look at this Java Tutorial ? 您看过此Java教程了吗?
Further, I'd advise you to stay the heck away from playing with this kind of stuff in real software. 此外,我建议您不要玩真正的软件中的这类东西。 It's good to play with it so you know what it is, but concurrency has pitfalls all over the place.
使用它很好,这样您就可以知道它是什么,但是并发性到处都是陷阱。 It's better to use higher level abstractions and synchronized collections or JMS queues if you are building software for other people.
如果要为其他人构建软件,最好使用更高级别的抽象和同步的集合或JMS队列。
That is at least what I do. 至少那是我要做的。 I'm not a concurrency expert so I stay away from handling threads by hand wherever possible.
我不是并发专家,所以我尽量避免手动处理线程。
Example 例
public class myThread extends Thread{
@override
public void run(){
while(true){
threadCondWait();// Circle waiting...
//bla bla bla bla
}
}
public synchronized void threadCondWait(){
while(myCondition){
wait();//Comminucate with notify()
}
}
}
public class myAnotherThread extends Thread{
@override
public void run(){
//Bla Bla bla
notify();//Trigger wait() Next Step
}
}
Example for wait() and notifyall() in Threading. Threading中的wait()和notifyall()的示例。
A synchronized static array list is used as resource and wait() method is called if the array list is empty. 同步的静态数组列表用作资源,如果数组列表为空,则调用wait()方法。 notify() method is invoked once a element is added for the array list.
为数组列表添加元素后,将调用notify()方法。
public class PrinterResource extends Thread{
//resource
public static List<String> arrayList = new ArrayList<String>();
public void addElement(String a){
//System.out.println("Add element method "+this.getName());
synchronized (arrayList) {
arrayList.add(a);
arrayList.notifyAll();
}
}
public void removeElement(){
//System.out.println("Remove element method "+this.getName());
synchronized (arrayList) {
if(arrayList.size() == 0){
try {
arrayList.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
arrayList.remove(0);
}
}
}
public void run(){
System.out.println("Thread name -- "+this.getName());
if(!this.getName().equalsIgnoreCase("p4")){
this.removeElement();
}
this.addElement("threads");
}
public static void main(String[] args) {
PrinterResource p1 = new PrinterResource();
p1.setName("p1");
p1.start();
PrinterResource p2 = new PrinterResource();
p2.setName("p2");
p2.start();
PrinterResource p3 = new PrinterResource();
p3.setName("p3");
p3.start();
PrinterResource p4 = new PrinterResource();
p4.setName("p4");
p4.start();
try{
p1.join();
p2.join();
p3.join();
p4.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Final size of arraylist "+arrayList.size());
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.