防护块 - notifyAll()vs interrupt()

时间:2014-10-05 19:46:23

标签: java multithreading concurrency synchronization locking

此Q查找以下内容的验证和/或评论/意见:

Guarded Blocks上的示例如下:

public synchronized void guardedJoy() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

此代码的另一端 - 正确设置joy就是这样:

public void setJoy2(TheClass t) {
    synchronized (t) {
        t.joy = true; 
        t.notifyAll();
    }
}

以上是"信令"通过使用notify()来获得快乐。

另一种方法是管理这个"信令" interrupt()

public void guardedJoy2() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.

    while(!joy) {
        synchronized(this) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
    }
    System.out.println("Joy and efficiency have been achieved!");
}

和一个设置joy并让线程等待它:

public void setJoy2(TheClass t) {
    t.joy = true; 
    t.interrupt(); 
}

我希望对两者进行比较 - setJoy()setJoy2()

首先,上面guardedJoy2()可以"听到"正确地setJoy()setJoy2() - 可以看到joy何时设置并按照预期的方式行事(?) guardedJoy2()guardedJoy()相比如何? 它实现与guardedJoy()完全相同的东西 - 我可能会遗漏一些东西,但我没有看到结果的差异。唯一的区别是guardedJoy2()从循环内释放this的锁定,而其他人在方法终止之前获取它以获得一些意外结果。将它放在一边(即,假设这是joy及其副作用在代码中出现的唯一地方),guardedJoy()和{{1}之间没有区别}(?)

guardedJoy2()回复guardedJoy2()setJoy()。 它可以听到"从setJoy2()完成后,重新获得锁并从那里开始。 而且,它可以听到"来自setJoy() - 通过接收中断并因此抛出setJoy2()以退出InterruptedException,这也是同步声明的结束,检查以查看wait()条件while已设置并从那里开始。如果中断来自"某人"否则,而不是从一个设置joy,再次以相同的方式进入循环,直到设置joy

何时调用joy,因此在wait()中释放了this的锁定, 一些其他线程可以通过获取此锁来进入并执行在guardedJoy2()设置并且joy应该正确返回之前不应该执行的操作。但是,将此放在一边(再次,假设这不是一个问题 - 唯一需要注意的是在控制台的guardedJoy2()的最后一行看到该消息。)这个 - {{1如果对象在其guardedJoy2()设置并从那里开始(在setJoy2()中,线程设置joy,则可以优先考虑其他事情没有必要让对象锁定来中断它,而setJoy2()应该有锁定来调用它上面的joy

setJoy()& notifyAll()guardedJoy2()& setJoy2()以上?

TIA。

1 个答案:

答案 0 :(得分:0)

我假设你的第一个notify()只是setJoy而不是notifyAll().

首先,需要注意的是,如果您在interrupt()类型的表达式上调用TheClass,则TheClass是{{1}的子类}}。这需要against一些-recommendations,表明您应该使用Thread实例来封装要在线程上运行的逻辑,而不是继承类Runnablejavadoc of Thread#join(int)也声明了

  

建议应用程序不要在Thread个实例上使用waitnotifynotifyAll

这是因为Java的某些实现使用这些方法来处理幕后的线程逻辑。如果您不知道该实现逻辑并将这些方法与Thread实例一起使用,则可能会出现意外行为。

然后,这可能需要进行一些分析,抛出(创建)异常是expensive operation,可能比removing a Thread from an object's wait set更多。更重要的是,异常应该用于特殊情况,而不是指导您的应用程序逻辑。

我打算说你的第二个示例的同步顺序可能不正确(假设Thread不是joy),因为volatile循环中的读取可能看不到写入guardedJoy2。但是,Java语言规范说明

  

如果线程T1中断线程T2,则T1中断   同步 - 任何其他线程(包括T2)的任何一点   确定T2已被中断(通过具有   setJoy2抛出或调用InterruptedException或。{   Thread.interrupted

所以你仍然有可见性保证。