同一对象上的同步块是否确实阻止其他线程执行该块?

时间:2014-03-03 12:49:22

标签: java multithreading wait synchronized

您好我正在尝试理解等待通知,我在代码中看到这种行为,即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

3 个答案:

答案 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();
        }
    }
}