分享
 
 
 

Java Thread Programming 1.8.1 - Inter-thread Communication

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

The Need for Inter-thread Signaling

Through synchronization, one thread can safely change values that another thread will read. How does the second thread know that the values have changed? What if the second thread is waiting for the values to change by rereading the values every few seconds?

One not-so-good way that a thread can wait for a value to change is by using a busy/wait:

while ( getValue() != desiredValue ) {

Thread.sleep(500);

}

Such code is called a busy/wait because the thread is busy using up processor resources to continually check to see if the value has changed. To use fewer resources, the sleep time could be increased, but then the thread might not find out about the change for quite some time. On the other hand, if the sleep time is reduced, the thread will find out sooner, but will waste even more of the processor resources. In Java, there is a much better way to handle this kind of situation: the wait/notify mechanism.

有时候我们需要线程间的通讯,比如第二个线程如何知道第一个线程的某些值发生了改变?不太好的方法如上,称之为busy/wait,通过不断循环并结合Thread.sleep()测试值是否发生变化,会占用处理器资源,并且循环的频率不容易掌握,快了浪费资源,慢了降低反应速度。像这种情况,java中给出了一种更好的解决方法:wait/notify机制

The Wait/Notify Mechanism

The wait/notify mechanism allows one thread to wait for a notification from another thread that it may proceed.

Minimal Wait/Notify

At a bare minimum, you need an object to lock on and two threads to implement the wait/notify mechanism.

Imagine that there is a member variable, valueLock, that will be used for synchronization:

private Object valueLock = new Object();

The first thread comes along and executes this code fragment:

synchronized ( valueLock ) {

try {

valueLock.wait();

} catch ( InterruptedException x ) {

System.out.println(“interrupted while waiting”);

}

}

The wait() method requires the calling thread to have previously acquired the object-level lock on the target object. In this case, the object that will be waited upon is valueLock, and two lines before the valueLock.wait() statement is the synchronized(valueLock) statement. The thread that invokes the wait() method releases the object-level lock and goes to sleep until notified or interrupted. If the waiting thread is interrupted, it competes with the other threads to reacquire the object-level lock and throws an InterruptedException from within wait(). If the waiting thread is notified, it competes with the other threads to reacquire the object-level lock and then returns from wait().

Wait()方法需要在获得object-level lock之后才能调用,否则会抛出IllegalMonitor-

StateException异常,当线程调用wait()方法后,会释放object-level lock,然后sleep(),直到被notified或interrupted,如果被interrupted,此线程会重新竞争获得object-level lock,然后抛出InterrruptedException,如果被notified,此线程会重新竞争获得object-level lock,从wait()返回,继续执行wait()以后的代码。

Many times, a thread is interrupted to signal it that it should clean up and die (see Chapter 5). The statements used to wait can be slightly rearranged to allow the InterruptedException to propagate up further:

try {

synchronized ( valueLock ) {

valueLock.wait();

}

} catch ( InterruptedException x ) {

System.out.println(“interrupted while waiting”);

// clean up, and allow thread to return from run()

}

有时候,中断一个线程是为了将其销毁清除,可以将InterruptedException异常延迟处理

Instead of catching InterruptedException, methods can simply declare that they throw it to pass the exception further up the call chain:

public void someMethod() throws InterruptedException {

// ...

synchronized ( valueLock ) {

valueLock.wait();

}

// ...

}

如果不在方法内捕捉异常,可以继续上抛,留待以后处理

The thread doing the notification comes along and executes this code fragment:

synchronized ( valueLock ) {

valueLock.notify(); // notifyAll() might be safer...

}

This thread blocks until it can get exclusive access to the object-level lock for valueLock. After the lock is acquired, this thread notifies one of the waiting threads. If no threads are waiting, the notification effectively does nothing. If more than one thread is waiting on valueLock, the thread scheduler arbitrarily chooses one to receive the notification. The other waiting threads are not notified and continue to wait. To notify all waiting threads (instead of just one of them), use notifyAll() (discussed later in this chapter).

首先要获得object-level lock,然后调用notify()通知此object-level lock上所有等待线程中的一个,如果没有waiting线程,则通知无效。如果有多个等待线程,则线程调用机制选择其中一个通知,其它没有获得通知的继续等待。如果要通知所有此锁上的等待线程,适用notifyAll()

Typical Wait/Notify

In most cases, a member variable is checked by the thread doing the waiting and modified by the thread doing the notification. The checking and modification occur inside the synchronized blocks to be sure that no race conditions develop.

大部分情况下,都是一个线程等待一个成员变量满足某个条件,另一个线程修改此成员变量后进行通知。

This time, two member variables are used:

private boolean value = false;

private Object valueLock = new Object();

The value variable is checked by the thread doing the waiting and is set by the thread doing the notification. Synchronization on valueLock controls concurrent access to value.

The first thread comes along and executes this code fragment:

try {

synchronized ( valueLock ) {

while ( value != true ) {

valueLock.wait();

}

// value is now true

}

} catch ( InterruptedException x ) {

System.out.println(“interrupted while waiting”);

}

After acquiring the object-level lock for valueLock, the first thread checks value to see if it is true. If it is not, the thread executes wait(), releasing the object-level lock. When this thread is notified, it wakes up, reacquires the lock, and returns from wait(). To be sure that it was not falsely notified (see the “Early Notification” discussion later in this chapter), it re-evaluates the while expression. If value is still not true, the thread waits again. If it is true, the thread continues to execute the rest of the code inside the synchronized block.

While the first thread is waiting, a second thread comes along and executes this code fragment:

synchronized ( valueLock ) {

value = true;

valueLock.notify(); // notifyAll() might be safer...

}

When the first thread executes the wait() method on valueLock, it releases the object-level lock it was holding. This release allows the second thread to get exclusive access to the object-level lock on valueLock and enter the synchronized block. Inside, the second thread sets value to true and invokes notify() on valueLock to signal one waiting thread that value has been changed.

Wait/Notify with synchronized Methods

Sometimes, the class is designed to synchronize on this instead of another object. In this case, the synchronized method modifier can be used instead of a synchronized statement. The following code fragments are an adaptation of the previous example.

如果不是同步其它对象,而是同步this,可以类定义中结合wait/notify机制使用synchronized method。

As before, a member variable value is initially set to false:

private boolean value = false;

The first thread (threadA) comes along and invokes this waitUntilTrue() method:

public synchronized void waitUntilTrue()

throws InterruptedException {

while ( value == false ) {

wait();

}

}

While threadA is blocked on the wait(), a second thread (threadB) comes along and executes this method, passing in true for newValue:

public synchronized void setValue(boolean newValue) {

if ( newValue != value ) {、

value = newValue;

notify(); // notifyAll() might be safer...

}

}

Note that both methods are synchronized and are members of the same class. In addition, both threads are invoking methods on the same instance of this class. The waitUntilTrue() method (with the wait() inside) declares that it might throw an InterruptedException. In this case, when threadB passes in true, value is changed and notify() is used to signal the waiting threadA that it may proceed. threadA wakes up, reacquires the object-level lock on this, returns from wait(), and re-evaluates the while expression. This time, value is true, and threadA will return from waitUntilTrue().

如上,类定义中涉及到wait和notify的两个方法都被定义成synchronized,如果两个线程都是调用此类的同一个实例,则两个线程间可以互相通信。

Object API Used for Wait/Notify

The wait/notify mechanism is embedded deep in the heart of Java. Object, the superclass of all classes, has five methods that are the core of the wait/notify mechanism: notify(), notifyAll(), wait(), wait(long), and wait(long, int). All classes in Java inherit from Object, so all classes have these public methods available to them. Additionally, none of these methods can be overridden in a subclass as they are all declared final.

Java中所有类的父类Object内置了wait/notify机制,它内置wait/notify机制的五种核心方法:

notify(),notifyAll(),wait(),wait(long),wait(long,int),所以java中的所有类都具有这五种方法,这五种方法是public,并且final,不能被子类覆盖。

notify()

public final native void notify()

throws IllegalMonitorStateException // RuntimeException

The notify() method is used by a thread to signal any other threads that might be waiting on the object. If more than one thread is waiting on the object, the thread scheduler will arbitrarily choose exactly one to be notified, and the others will continue to wait. If no threads are currently waiting on the object, notify() has no effect. Before invoking notify(), a thread must get exclusive access to the object-level lock for the object. Unlike wait(), the invocation of notify() does not temporarily release the lock. If the proper lock is not held when notify() is called, an IllegalMonitorStateException is thrown. This exception is a subclass of RuntimeException, so a try-catch construct is not necessary and is rarely used.

notify()方法用来通知在其信号量上等待的所有其它线程。如果有一个以上的线程等待,则会选择其中一个进行通知,其它继续等待,如果没有线程等待,则此方法无效。调用notify()并不象wait()那样释放锁,而是等待锁完成后自己释放。如果调用notify()是没有持有适当的锁,会抛出IllegalMonitorStateException(RuntimeException的子类,不必try/catch)

notifyAll()

public final native void notifyAll()

throws IllegalMonitorStateException // RuntimeException

The notifyAll() method works the same as notify() (see above) with one important exception: When notifyAll() is invoked, all the threads waiting on the object are notified, not just one. The advantage of notifyAll() is that you don’t have to be concerned about which one of the waiting threads will be notified—they will all be notified. The disadvantage is that it might be wasteful (in terms of processor resources) to notify all the waiting threads if only one will actually be able to proceed. When in doubt, err on the side of safety over speed and use notifyAll() instead of notify().

notifyAll()通知锁上的所有等待线程,其缺点显而易见,如果没有必要通知所有等待线程,可能会浪费处理器资源。基于安全而不是速度考虑,应该使用notifyAll(),而不是notify()。

wait()

public final void wait()

throws InterruptedException,

IllegalMonitorStateException // RuntimeException

The wait() method is used to put the current thread to sleep until it is notified or interrupted. Before invoking wait(), a thread must get exclusive access to the object-level lock for the object. Just after entering wait(), the current thread releases the lock. Before returning from wait(), the thread competes with the other threads to reacquire the lock. If the proper lock is not held when wait() is called, an IllegalMonitorStateException is thrown. This exception is a subclass of RuntimeException, so a try-catch construct is not necessary and is rarely used.

If the waiting thread is interrupted, it competes to reacquire the lock and throws an InterruptedException from within wait(). This exception is not a subclass of RuntimeException, so a try-catch construct is required.

wait()将当前线程sleep直至被notified或interrupted。

调用wait()之前,线程必须排他获得对象的object-level lock。

一进入wati(),当前线程就释放object-level lock。

在wait()返回之前,线程重新和其它线程竞争获得此锁。

如果没有持有适当的锁就调用了wait()会抛出IllegalMonitorStateException。

如果等待的线程被interrrupted,会重新获得此锁,然后抛出InterrruptedException,此异常非RuntimeException的子类,所以必须要try/catch。

wait(long)

public final native void wait(long msTimeout)

throws InterruptedException,

IllegalMonitorStateException, // RuntimeException

IllegalArgumentException // RuntimeException

The wait(long) method is used to put the current thread to sleep until it is notified, interrupted, or the specified timeout elapses. Other than the timeout, wait(long) behaves the same as wait() (see above). The argument msTimeout specifies the maximum number of milliseconds that the thread should wait for notification. If msTimeout is 0, the thread will never time out (just like wait()). If the argument is less than 0, an IllegalArgumentException will be thrown. IllegalArgumentException is a subclass of RuntimeException, so a try-catch block is not required and is rarely used.

让线程等待一定的时间,毫秒,如果参数设为0,则无时间限制,如果参数小于0,抛出IllegalArgumentException异常,此异常为RuntimeException的子类,不需要try-catch

If the specified number of milliseconds elapses before the waiting thread is notified or interrupted, it competes to reacquire the lock and returns from wait(long). There is no way for the caller to determine whether a notification or a timeout occurred because no information (void) is returned from wait(long).

如果设定的时间内等待线程没有被notified/interrupted,会重新获得锁然后从wait()返回。调用者没有办法知道是被通知还是超时,因为wait(long)没有返回值。

wait(long, int)

public final void wait(long msTimeout, int nanoSec)

throws InterruptedException,

IllegalMonitorStateException, // RuntimeException

IllegalArgumentException // RuntimeException

The wait(long, int) method works just like wait(long, int) (see above) with the exception that nanoseconds can be added to the timeout value. The argument nanoSec is added to msTimeout to determine the total amount of time that the thread will wait for notification before returning. A nanosecond is one-billionth of a second (10E-9), and most common implementations of the Java VM don’t truly support this fine a resolution of time. For this reason, the use of the method is currently quite rare.

同,wait(long),不过时间设置更精确,可设置纳秒数,不过一般虚拟机未实现此方法。

When to Use notifyAll() Instead of notify()

The fundamental difference between notify() and notifyAll() is that if more than one thread is simultaneously waiting for notification, notify() will provide notification to only one of the waiting threads, whereas notifyAll() will provide notification to all of them. If your code is well defended against early notifications (discussed later), notifyAll() is generally the better choice.

The major disadvantage of notifyAll() is that it is wasteful of processor resources if all but one of the notified threads will end up waiting again. This is a situation that is difficult to guarantee. If your code synchronizes on this either through synchronized blocks or the synchronized method modifier, you can’t be sure that some code external to the class won’t synchronize and wait on a reference to the object. If that happens, notify() might signal the thread running that external code instead of the thread that you intended. Consider a situation where you have a class, ClassX, with two methods:

同一个锁有不同的加锁方式,类内部给this加锁,外部类还可以给此类的实例加锁,多个线程等待此锁,notify()不一定会通知到期望的线程。

类ClassX中有以下两个synchronized方法,其中包括了wait/notify

public synchronized void waitUntilTrue()

throws InterruptedException {

while ( value == false ) {

wait();

}

}

public synchronized void setValue(boolean newValue) {

if ( newValue != value ) {

value = newValue;

notify(); // notifyAll() might be safer...

}

}

同时,有个外部类ClassY,生成ClassX的实例,也此实例上wait()

In addition, there’s an external class, ClassY, with this code in one of its methods:

ClassX cx = new ClassX();

cx.setValue(false);

// ...

synchronized ( cx ) {

cx.wait(); // trouble

}

If threadA is running inside ClassY, it synchronizes on cx and invokes wait(). If threadB invokes waitUntilTrue(), it is now also waiting for notification. If threadC invokes setValue() and passes true (a new value), threadC will only notify one thread because notifyAll() wasn’t used. There’s no way to be sure whether threadA or threadB will be notified. In this situation, notifyAll() would have guaranteed that they would both be notified.

此时如果有三个线程:threadA在ClassY运行,同步cx并等待;threadB调用cx的waitUntilTrue()等待;threadC调用cx的setValue(),它只能通知threadA,B中的一个,没有方法确定哪一个被通知。notifyAll()保证二者都被通知。

It is generally safe to use notify() only when you can guarantee that only one thread will ever be waiting for notification. This is a relatively unusual occurrence.

只有一个线程时,使用notify()

If you’re not sure whether you need to use notify() or notifyAll(), use notifyAll(). It might be a little wasteful, but it’s safer.

如果不确定使用哪一个,就使用notifyAll()吧

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有