Thread Signaling
2016-01-31 12:37
555 查看
本文大概意思都是都下边链接文章转换过来的,没有进行一字一句的翻译,只是把大概意思整里出来。
http://tutorials.jenkov.com/java-concurrency/thread-signaling.html
线程信号的目的是为了线程之间相互通信。线程信号可以使线程等待另其他线程信号,例如thread B 或许等待从thread A 发出的数据准备处理信号。
下面是一个简单的信号对象
thread A 与 thread B 必要通过同一个MySignal 实例来进行通信。如果 A B 引用了不同的MySignal实例,它们之间将不会接收到相互的信号。
或者变成inactive 状态。java 内嵌了使等待线程变成inactive状态的机制。使用java.lang.Object 上面的wait(),notify(),
notifyAll()可以实现。当一个线程在任意object 上调用wait(),它会变成inactive状态,直到有其他线程在该object上调用notify(),该线程才会继续执行。为了调用wait 或者notify ,调用线程必须获取在object上的锁。换句话说调用线程必须在同步块里调用wait ,notify,notifyAll。
下面是改造版的信号类MyWaitNotify
等待线程可以调用doWait,发出通知的线程可以调用doNotify。当一个线程在某个object上调用notify,所有在该object等待的线程,只有一个线程将会被唤醒继续执行代码。如果调用notifyAll可以唤醒该对象上所有等待的线程。所有的等待线程 ,通知线程都必须在synchronized 中调用wait,notify ,notifyAll。这是强制的。一个线程如果没有获取一个object上的锁,将不能调用这几个方法。否则会抛出IllegalMonitorStateException异常。一旦一个线程调用wait ,它将释放它所持有的monitor object上的锁。这样就可以允许其他线程调用同步块中的wait 或者notify 。当一个线程未离开notify同步块之前,唤醒的线程不能退出wait调用块,因为没有获得monitor object 上的锁。
下面是会存储信号的MyWaitNotify类
改成while循环后,即使发生虚假唤醒,如果wasSignalled的值没有被改变,那么线程将再次调用wait(),使线程变为inactive状态。
本文链接: http://my.oschina.net/robinyao/blog/611885
http://tutorials.jenkov.com/java-concurrency/thread-signaling.html
线程信号的目的是为了线程之间相互通信。线程信号可以使线程等待另其他线程信号,例如thread B 或许等待从thread A 发出的数据准备处理信号。
1 Signaling via Shared Objects
线程之间可以通过共享对象来相互发送信号。Thread A 可以通过在synchronized同步块里设置变量 hasDataToProcess为true ,线程B同样在synchronized同步块里读取hasDataToProcess的值来确定是否有数据可读。下面是一个简单的信号对象
public class MySignal{ protected boolean hasDataToProcess = false; public synchronized boolean hasDataToProcess(){ return this.hasDataToProcess; } public synchronized void setHasDataToProcess(boolean hasData){ this.hasDataToProcess = hasData; } }
thread A 与 thread B 必要通过同一个MySignal 实例来进行通信。如果 A B 引用了不同的MySignal实例,它们之间将不会接收到相互的信号。
2 Busy Wait
由于thread B 一直在等待是否有数据可以处理,如果采用上面的MySignal实例,那么thread B 必须一直循环调用hasDataToProcess来判断是否有数据处理。这样就会产生忙等,消耗大量的CPU。3 wait(), notify() and notifyAll()
忙等除了在平均时间较短的情况下比较有效,其他情况不能有效的利用CPU。如果线程在收到信号之前可以sleep,或者变成inactive 状态。java 内嵌了使等待线程变成inactive状态的机制。使用java.lang.Object 上面的wait(),notify(),
notifyAll()可以实现。当一个线程在任意object 上调用wait(),它会变成inactive状态,直到有其他线程在该object上调用notify(),该线程才会继续执行。为了调用wait 或者notify ,调用线程必须获取在object上的锁。换句话说调用线程必须在同步块里调用wait ,notify,notifyAll。
下面是改造版的信号类MyWaitNotify
public class MyWaitNotify{ Object myMonitorObject = new Object(); public void doWait(){ synchronized(myMonitorObject){ try{ myMonitorObject.wait(); } catch(InterruptedException e){...} } } public void doNotify(){ synchronized(myMonitorObject){ myMonitorObject.notify(); } } }
等待线程可以调用doWait,发出通知的线程可以调用doNotify。当一个线程在某个object上调用notify,所有在该object等待的线程,只有一个线程将会被唤醒继续执行代码。如果调用notifyAll可以唤醒该对象上所有等待的线程。所有的等待线程 ,通知线程都必须在synchronized 中调用wait,notify ,notifyAll。这是强制的。一个线程如果没有获取一个object上的锁,将不能调用这几个方法。否则会抛出IllegalMonitorStateException异常。一旦一个线程调用wait ,它将释放它所持有的monitor object上的锁。这样就可以允许其他线程调用同步块中的wait 或者notify 。当一个线程未离开notify同步块之前,唤醒的线程不能退出wait调用块,因为没有获得monitor object 上的锁。
4 Missed Signals
notify,notifyAll 不保存对它们的方法调用当没有线程等待在该monitor object。notify 信号就丢死了。因此,如果一个线程在调用wait之前调用notify,信号将会被等待线程丢失。这样在一些案例中将导致等待线程一直在等待,不会醒来,因为通知信号被丢失了。为了避免信号的丢失,信号可以被存储在signal类中。下面是会存储信号的MyWaitNotify类
public class MyWaitNotify2{ MonitorObject myMonitorObject = new MonitorObject(); boolean wasSignalled = false; public void doWait(){ synchronized(myMonitorObject){ if(!wasSignalled){ try{ myMonitorObject.wait(); } catch(InterruptedException e){...} } //clear signal and continue running. wasSignalled = false; } } public void doNotify(){ synchronized(myMonitorObject){ wasSignalled = true; myMonitorObject.notify(); } } }
5 Spurious Wakeups
虚假唤醒指的是即使线程没有调用监视器对象上的notify或者notifyAll,等待线程就醒了。唤醒可能没有任何原因的发生。如果一个虚假唤醒发生在MyWaitNofiy2中的doWait()中,等待线程在没有接收到一个恰当的信号就开始执行。这会导致严重的后果。下边把MyWaitNotify2中的if 判断改为while循环,只有wasSignalled状态改变,才认为是notify真正的被发出。public class MyWaitNotify3{ MonitorObject myMonitorObject = new MonitorObject(); boolean wasSignalled = false; public void doWait(){ synchronized(myMonitorObject){ while(!wasSignalled){ try{ myMonitorObject.wait(); } catch(InterruptedException e){...} } //clear signal and continue running. wasSignalled = false; } } public void doNotify(){ synchronized(myMonitorObject){ wasSignalled = true; myMonitorObject.notify(); } } }
改成while循环后,即使发生虚假唤醒,如果wasSignalled的值没有被改变,那么线程将再次调用wait(),使线程变为inactive状态。
6 Multiple Threads Waiting for the Same Signals
while 循环同样在多线程等待的情况中仍是一个很好的解决方案。当监视器上的notifyAll被调用时,只有其中一个线程会被允许继续执行,其他的将会被再次等待,因为其他线程获得锁后,wasSignalled上的信号已经被第一个唤醒的线程擦除掉了。7 Don't call wait() on constant String's or global objects
当线程把一个把常量字符串当作监视器对象时,会出现异常。原因是JVM/Compiler内部会把常量字符串转换为同一对象对待。这意味着即使你有两个不同的MyWaitNotify实例,它们里面的监视器对象将会是同一个字符对象。这意味着如果一个线程在第一信号实例调用wait方法,可能将会被另一信号实例上notify的调用唤醒。因此不要把全局对象,string 常量当作监视器对象用。本文链接: http://my.oschina.net/robinyao/blog/611885
相关文章推荐
- java-模拟tomcat服务器
- C#多线程之Thread中Thread.IsAlive属性用法分析
- 深入多线程之:Wait与Pulse的使用详解
- Android开发笔记之:如何安全中止一个自定义线程Thread的方法
- java thread start()和run()方法简析
- Java中Runnable和Thread的区别分析
- Java的wait(), notify()和notifyAll()使用心得
- Java线程中sleep和wait的区别详细介绍
- Android开发笔记之:Handler Runnable与Thread的区别详解
- C#多线程之Thread中Thread.Join()函数用法分析
- Mysql Error Code : 1436 Thread stack overrun
- 深入探讨:unix多进程编程之wait()与waitpid()函数
- MySQL错误Forcing close of thread的两种解决方法
- c#线程Thread示例
- 深入多线程之:用Wait与Pulse模拟一些同步构造的应用详解
- Android线程管理之ActivityThread
- C#中sleep和wait的区别分析
- 基于Java多线程notify与notifyall的区别分析
- 线程操作类
- 线程操作类