您好我正在尝试理解等待通知,我在代码中看到这种行为,即2个线程正在同一块上的同步块内打印语句。
public class WaitNotifyExample {
/**
* @param args
*/
public static void main(String[] args) {
Message msg = new Message("process it");
Waiter waiter = new Waiter(msg);
new Thread(waiter,"waiter").start();
Waiter waiter1 = new Waiter(msg);
new Thread(waiter1, "waiter1").start();
Notifier notifier = new Notifier(msg);
new Thread(notifier, "notifier").start();
//System.out.println("All the threads are started");
}
}
class Message {
private String msg;
public Message(String str){
this.msg=str;
}
public String getMsg() {
return msg;
}
public void setMsg(String str) {
this.msg=str;
}
}
class Waiter implements Runnable{
private Message msg;
public Waiter(Message m){
this.msg=m;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (msg) {
try{
System.out.println(name+" waiting to get notified at time:"+System.currentTimeMillis());
msg.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(name+" waiter thread got notified at time:"+System.currentTimeMillis());
//process the message now
System.out.println(name+" processed: "+msg.getMsg());
}
}
}
class Notifier implements Runnable {
private Message msg;
public Notifier(Message msg) {
this.msg = msg;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name+" started");
try {
Thread.sleep(1000);
synchronized (msg) {
msg.setMsg(name+" Notifier work done");
//msg.notify();
msg.notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
以下是该计划的输出:
-waiter waiting to get notified at time:1393849891481 -notifier started -waiter1 waiting to get notified at time:1393849891483 -waiter1 waiter thread got notified at time:1393849892483 -waiter1 processed: notifier Notifier work done -waiter waiter thread got notified at time:1393849892483 waiter processed: notifier Notifier work done
答案 0 :(得分:2)
阅读Object.wait
上的Javadoc:
线程释放此监视器的所有权并等待,直到另一个线程通知等待此对象监视器的线程
虽然线程在msg.wait
内被阻止,但它不拥有msg
的监视器。任何其他线程都可以自由获取它。
答案 1 :(得分:0)
代码按预期工作:没有单个线程干扰您的消息输出,否则(如果流内部未同步)这些字母和单词将完全混淆并打印乱码。
然而,一旦你调用wait,你就把当前线程置于保持状态并释放监视器上的锁,这样其他线程就可以在此期间打印出来。
另一件事你的代码很好地证明了:由于线程的时序是未定义的,可能会发生在调用notifyAll()
时,一个,多个或零个线程实际上在等待。如果线程B在线程A等待之前已经调用notify
,那么这是常见的等待/通知问题之一。
另外请注意,synchronized / wait / notify是一种非常基本的“强力”线程同步方法,它带来许多陷阱,并且由于许多线程通常被搁置,因此速度也不是很快。它可以用于基本和简单的代码,但如果你真的想深入了解线程,你不应该使用它,而是使用concurrent package及其类和功能。例如,可以使用LinkedBlockingQueue<String>
而不是使用synchronized来编写相同的代码。
答案 2 :(得分:0)
确定。我认为重点已经涵盖了wait()释放监视器/锁定所以其他线程可以进入同步块。但我想补充两点。 1.当使用wait()时,不要忘记检查您应该等待的时间或条件。您可能成为虚假唤醒呼叫的受害者。检查此链接 http://handling-thread.blogspot.co.uk/2012/11/what-is-spurious-wakeup-while-wait-in.html 2.如果您正在考虑等待通知方法,我建议您使用相同方法的最新解决方案,即使用Condition.await-signal
供我参考,我已通过条件方法更新了代码。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class WaitNotifyExample {
/**
* @param args
*/
public static void main(String[] args) {
Message msg = new Message("process it");
Waiter waiter = new Waiter(msg);
new Thread(waiter, "waiter").start();
Waiter waiter1 = new Waiter(msg);
new Thread(waiter1, "waiter1").start();
Notifier notifier = new Notifier(msg);
new Thread(notifier, "notifier").start();
//System.out.println("All the threads are started");
}
}
class Message {
final Lock lock = new ReentrantLock();
final Condition msgAvailable = lock.newCondition();
private String msg = null;
public Message(String str) {
this.msg = str;
}
public String getMsg() {
return msg;
}
public void setMsg(String str) {
this.msg = str;
}
public void lock() {
lock.lock();
}
public void await() throws InterruptedException {
msgAvailable.await();
}
public void unlock() {
lock.unlock();
}
public void signal() {
msgAvailable.signal();
}
public void signalAll() {
msgAvailable.signalAll();
}
}
class Waiter implements Runnable {
private Message msg;
public Waiter(Message m) {
this.msg = m;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
msg.lock();
try {
System.out.println(name + " waiting to get notified at time:" + System.currentTimeMillis() + " Object: " + msg);
//You missed while condition which is very important aspect of wait-notify. You can check this link
//http://handling-thread.blogspot.co.uk/2012/11/what-is-spurious-wakeup-while-wait-in.html
while(msg.getMsg() == null)
msg.await();
//msg.wait();
System.out.println(name + " waiter thread got notified at time:" + System.currentTimeMillis() + " Object: " + msg);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
msg.unlock();
}
//process the message now
System.out.println(name + " processed: " + msg.getMsg());
}
}
class Notifier implements Runnable {
private Message msg;
public Notifier(Message msg) {
this.msg = msg;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + " started");
msg.lock();
try {
Thread.sleep(1000);
msg.setMsg(name + " Notifier work done");
//msg.notify();
msg.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
msg.unlock();
}
}
}