对java同步的工作方式感到困惑

时间:2014-12-28 05:58:40

标签: java multithreading synchronization

我有基于我在网络上找到的示例的示例代码,以说明如何使用wait()notify()。让我在代码示例前面加上Java教科书对waitnotify所做的以下陈述。

  

线程不能在对象上调用wait或notify方法,除非它   拥有该对象的锁定

现在看看这段代码和输出:

public class ThreadTester
{
    public 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;} 
    }

    public class Waiter implements Runnable{         
        private Message msg;
        public Waiter(Message m){
            this.msg = m;
        }
        public void run() {
            String name = Thread.currentThread().getName();
            synchronized (msg) {
                try{
                    System.out.println(name + " acquired lock to msg object. waiting to get notified");
                    msg.wait();
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(name + " waiter done at time: " + System.currentTimeMillis());
            }
            System.out.println(name + " waiter giving up msg object lock");
        }
    }

    public class Notifier implements Runnable {      
        private Message msg;         
        public Notifier(Message msg) {
            this.msg = msg;
        }
        public void run() {
            String name = Thread.currentThread().getName();
            System.out.println(name + " started");
            try {
                Thread.sleep(5000);
                synchronized (msg) {
                    String localMesg = name + " acquired lock to msg object. Notifier work done.";
                    System.out.println(localMesg);
                    msg.setMsg(localMesg);
                    msg.notify();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }    
        }    
    }

    void runtest() {
        Message msg = new Message("process it");
        Waiter waiter1 = new Waiter(msg);
        new Thread(waiter1,"waiter1").start();

        Waiter waiter2 = new Waiter(msg);
        new Thread(waiter2, "waiter2").start();

        Notifier notifier = new Notifier(msg);
        new Thread(notifier, "notifier").start();
        System.out.println("All the threads are started");
    }

    public static void main(String [] args) {
        new ThreadTester().runtest();
    }
}

这是输出:

waiter1 acquired lock to msg object. waiting to get notified
waiter2 acquired lock to msg object. waiting to get notified
All the threads are started
notifier started
notifier acquired lock to msg object. Notifier work done.
waiter1 waiter done at time: 1419745413721
waiter1 waiter giving up msg object lock

现在问题是,当waiter2线程还在持有时,notifierwaiter1线程如何获取msg对象的锁定?这似乎与我上面引用的Java教科书声明直接相矛盾。我错过了什么?

感谢您的帮助。

2 个答案:

答案 0 :(得分:6)

在模式中

synchronized (msg) {
  msg.wait();
}

msg语句确实占用synchronized上的锁。但msg.wait()调用会暂时释放锁定并等待通知。稍后的msg.notify()msg.notifyAll()可以满足该等待。等待结束后,再次锁定msg。在示例中,通告程序线程执行msg.notify(),这意味着两个服务器线程中的一个可以满足其msg.wait()。这不会立即发生,因为msg.notify()位于synchronized (msg)块内,因此通告程序线程正在msg上保持锁定。但是,只要该块退出,无论哪个帖子msg.wait()收到通知,都会收回msg上的锁定并继续。

这种模式相当脆弱。通告程序线程首先进行5秒睡眠,以确保两个服务员线程都已达到msg.wait()。如果没有睡眠,Notifier可能会在Waiter线程完成msg.notify()之前执行msg.wait(),而msg.notify()将没有效果。由于存在这样的问题,通常最好使用Semaphore包中的java.util.concurrent等同步类,而不是使用synchronizedwait / notify

答案 1 :(得分:2)

等待时按住锁是没有意义的。如果您在等待时持有锁,则无法通知它。

根据§17.2.1 (Wait) of the JLS

将线程t添加到对象m的等待集中,并对m执行n次解锁操作。

因此,这意味着当线程在等待通知时,锁定被释放,因此通知程序可以获取锁定并消耗等待集。