您的位置:首页 > 编程语言 > Java开发

wait(), notify() and notifyAll() in Java - A tutorial

2011-12-02 09:02 399 查看
The use of the implicit monitors in Java objects is powerful, but you can achieve a more subtle level of control throughinter-process communication. As you will see, this is especially easy in Java.
Multithreading replaces event loop programming by dividing your tasks into discrete and logical units. Threads also provide a secondary benefit: they do away with polling. Polling is usually implemented by a loop thatis used to check some condition repeatedly.
Once the condition is true, appropriate action is taken. This wastes CPU time. For example, consider the classic queuing problem,where one thread is producing some data and another is consuming it. To make the problem more interesting, suppose that the producer
has to wait until the consumer isfinished before it generates more data. In a polling system, the consumer would waste many CPU cycles while it

waited for the producer to produce. Once the producer wasfinished, it would start polling, wasting more CPU cycles waiting for the consumer to finish, andso on. Clearly, this situation is undesirable.

To avoid polling, Java includes an elegant interrocesscommunication mechanism via thewait( ),
notify( ), and notifyAll( ) methods.These methods are implemented as
finalmethods in Object, so all classes have them. All threemethods can be called only from within asynchronized
method. Although conceptuallyadvanced from a computer science perspective, the rules for using these methods areactually quite simple:

wait( ) tells the calling thread to give up the monitorand go to sleep until some other

thread enters the same monitor and calls notify( ).
notify( ) wakes up the first thread that called wait()on the same object.
notifyAll( ) wakes up all the threads that called wait()on the same object. The

highest priority thread will run first.

These methods are declared within Object, as shown here:

final void wait( ) throws InterruptedException

final void notify( )

final void notifyAll( )

Additional forms of wait( ) exist that allow you tospecify a period of time to wait. The following sample program incorrectly implements a simpleform of the producer/consumer problem. It consists of four classes:Q,the queue
that you're trying to synchronize; Producer, the threaded object that isproducing queue entries;Consumer, the threaded object that is consuming queue entries; andPC,the tiny class that creates the single
Q, Producer, and Consumer.


// An incorrect implementation of a producer and consumer.

class Q {

int n;

synchronized int get() {

System.out.println("Got: " + n);

return n;

}

synchronized void put(int n) {

this.n = n;

System.out.println("Put: " + n);

}

}

class Producer implements Runnable {

Q q;

Producer(Q q) {

this.q = q;

new Thread(this, "Producer").start();

}

public void run() {

int i = 0;

while(true) {

q.put(i++);

}

}

}

class Consumer implements Runnable {

Q q;

Consumer(Q q) {

this.q = q;

new Thread(this, "Consumer").start();

}

public void run() {

while(true) {

q.get();

}

}

}

class PC {

public static void main(String args[]) {

Q q = new Q();

new Producer(q);

new Consumer(q);

System.out.println("Press Control-C to stop.");

}

}

Although the put( ) and get( ) methods on
Q
aresynchronized, nothing stops the producer from overrunning the consumer, nor will anything stopthe consumer from consuming the same queue value twice. Thus, you get theerroneous output shown here (the exact output will vary with processor speed
and task load):

Put: 1

Got: 1

Got: 1

Got: 1

Got: 1

Got: 1

Put: 2

Put: 3

Put: 4

Put: 5

Put: 6

Put: 7

Got: 7

As you can see, after the producer put 1, the consumer startedand got the same 1 five times in a row. Then, the producer resumed and produced 2through 7 without letting the consumer have a chance to consume them.

The proper way to write this program in Java is to use wait()
and notify( ) to signal in both directions, as shown here:


// A correct implementation of a producer and consumer.

import java.util.Random;

class Q {
int n;
boolean valueSet = false;

synchronized int get() {
if (!valueSet)
try {
wait();
} catch (InterruptedException e) {
System.out.println("InterruptedException caught");
}
System.out.println("Got: " + n);
valueSet = false;
notify();
return n;
}

synchronized void put(int n) {
if (valueSet)
try {
wait();
} catch (InterruptedException e) {
System.out.println("InterruptedException caught");
}
this.n = n;
valueSet = true;
System.out.println("Put: " + n);
notify();
}
}

class Producer implements Runnable {
Q q;

Producer(Q q) {
this.q = q;
new Thread(this, "Producer").start();
}

public void run() {
int i = 0;
while (true) {
q.put(i++);
}
}
}

class Consumer implements Runnable {
Q q;

Consumer(Q q) {
this.q = q;
new Thread(this, "Consumer").start();
}

public void run() {
while (true) {
q.get();
}
}
}

class TestMultiThread {
public static void main(String args[]) {
Q q = new Q();
new Producer(q);
new Consumer(q);
System.out.println("Press Control-C to stop.");
Random random=new Random();
TestThread thread1=new TestThread("1");
TestThread thread2=new TestThread("2");
thread1.start();
thread2.start();
int flag=0;
while(true){
flag=random.nextInt(10)+1;
if(flag%2==1)
thread1.setMax(flag);
else {
thread2.setMax(flag);
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

/**********************My new Demo for testing multiThread*************************************************/

class TestThread extends Thread{
int max;
String flag;
boolean valueSet = false;
public TestThread(String flag) {
// TODO Auto-generated constructor stub
this.flag=flag;
}
synchronized void printSet(){
if (!valueSet)
try {
wait();
} catch (InterruptedException e) {
System.out.println("InterruptedException caught");
}
for(int i=1;i<=max;i++){
System.out.println("Thread " + flag+" is running for "+i+" times");
}
valueSet = false;
notify();
}
synchronized void setMax(int n) {
if (valueSet)
try {
wait();
} catch (InterruptedException e) {
System.out.println("InterruptedException caught");
}
this.max = n;
valueSet = true;
System.out.println("Thread " + flag+" is setted to run "+n+" times");
notify();
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
printSet();
}

}
}


Inside get( ), wait( ) is called. This causes itsexecution to suspend until theProducernotifies you that some data is ready. When this happens,execution insideget( )resumes. After the data
has been obtained, get( ) calls notify(). This tells
Producer that it is okay to put more data in the queue. Insideput( ),
wait() suspends execution until theConsumerhas removed the item from the queue. When executionresumes, the next item of data is put in the queue, andnotify( )
iscalled. This tells the Consumer that it should now remove it.

Here is some output from this program, which shows the cleansynchronous behavior:

Put: 1

Got: 1

Put: 2

Got: 2

Put: 3

Got: 3

Put: 4

Got: 4

Put: 5

Got: 5
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: