Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java线程等待和通知方法_Java_Multithreading_Wait_Notify - Fatal编程技术网

Java线程等待和通知方法

Java线程等待和通知方法,java,multithreading,wait,notify,Java,Multithreading,Wait,Notify,我正在为OCJP学习,现在我在“线程”一章,我有一些关于等待和通知方法的问题。我想我理解这里发生的事情,但我只是想确保我走的是正确的道路。我写了以下代码作为示例: package threads; public class Main { static Object lock = new Object(); public static void main(String[] args) { new Main().new FirstThread().start()

我正在为OCJP学习,现在我在“线程”一章,我有一些关于等待和通知方法的问题。我想我理解这里发生的事情,但我只是想确保我走的是正确的道路。我写了以下代码作为示例:

package threads;

public class Main {

    static Object lock = new Object();

    public static void main(String[] args) {
        new Main().new FirstThread().start();
        new Main().new SecondThread().start();
    }

    class FirstThread extends Thread {
        public void run() {
            synchronized (lock) {
                lock.notify();
                System.out.println("I've entered in FirstThread");
            }
        }
    }
    class SecondThread extends Thread {
        public void run() {
            synchronized (lock) {
                try {
                    lock.wait();
                    System.out.println("I'm in the second thread");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
在本例中,控制台输出是我在FirstThread中输入的
,因为第一个线程启动,调用notify()方法,然后第二个线程启动,调用wait()方法,并且不打印字符串“我在第二个线程中”

下一个场景是反转
newmain().newfirstthread().start()的位置
新主线程().newsecondthread().start()输出为

I've entered in FirstThread
I'm in the second thread
因为第二个线程启动,wait()方法被调用,然后第一个线程启动,方法notify()被调用,控制台打印我在第一个线程中输入的
,等待被释放,并且
我在第二个线程中
被打印在控制台中

发生这种情况是因为计算机速度太快,线程按顺序运行吗?理论上,我认为可以首先调用第二个start()方法,是吗

最后一个问题是,为什么lock对象必须是静态的,因为如果我删除static修饰符,输出总是我在FirstThread中输入的


我知道加载类时,JVM中会加载静态字段,但我无法理解lock对象的逻辑。

线程是按顺序启动的,理论上线程1会在线程2之前执行,尽管它不保证执行。(尽管如此,在这个简单的例子中,它肯定是一致的,因为没有真实或模拟的延迟)

这就是为什么当线程2稍早启动时,它有机会等待一个随后得到通知(由线程1)的锁,而不是永远等待一个已经通知过一次的锁(因此,没有打印)

static
lock
Object
:您正在将
[第一/第二]线程
嵌套类绑定到
Main
实例,因此,如果希望它们在同一个锁上同步,则锁必须对两者通用

如果它是一个实例对象,那么线程将在不同的锁上访问和同步,因为
newmain()…
习惯用法将获得
Main
的两个实例,然后是
lock
的两个实例

“发生这种情况是因为计算机速度太快,线程也在运行吗?” 按顺序?理论上可以调用第二个start()方法 首先,我认为是吗?“

是的,为了更好的(单元)测试或演示,您可以引入随机时间的sleep()(当然,最终运行的代码不应该有这种睡眠)

最后一个问题是,为什么锁对象必须是静态的


原则上,锁是否是静态的并不重要,但您必须能够访问它,并且它必须是同一个锁对象。(每个类不能有一个对象实例)。在您的情况下,它必须是静态的,否则它将是两个不同的对象实例。

这是错误的,因为它不会更改其他线程可以测试的任何共享状态:

synchronized (lock) {
    lock.notify();
    System.out.println("I've entered in FirstThread");
}
这是错误的,因为它没有测试任何东西:

synchronized (lock) {
    lock.wait();
    System.out.println("I'm in the second thread");
}
问题是,
lock.notify()
如果没有线程在
lock.wait()
中休眠,则它根本不会执行任何操作。在您的程序中,
FirstThread
可以在
SecondThread
调用wait()之前调用notify()在那种情况下,打电话是不行的

他们让您在调用wait()或notify()之前输入互斥体(即
synchronized
块)是有原因的。这是因为您应该使用互斥体来保护服务员正在等待的共享状态

“共享状态”可以简单到一个布尔值:

 boolean firstThreadRan = false;
通知人(又称“制作人”)执行以下操作:

 synchronized(lock) {
     firstThreadRan = true;
     lock.notify();
     ...
 }
synchronized(lock) {
    while (! firstThreadRan) {
        lock.wait();
    }
    ...
}
服务员(又称“消费者”)会这样做:

 synchronized(lock) {
     firstThreadRan = true;
     lock.notify();
     ...
 }
synchronized(lock) {
    while (! firstThreadRan) {
        lock.wait();
    }
    ...
}

在这种情况下,while循环不是绝对必要的,但当多个消费者竞争同一事件时,它变得非常重要。始终使用循环是一种很好的做法。

非常感谢!我现在理解了逻辑。我已经更改了一些代码,现在它正按照我的预期工作
Main=new Main();main.new SecondThread().start();main.new FirstThread().start();
sleep()
永远不会修复并发错误。这只会降低并发错误发生的可能性。如果您是为了演示或教育目的编写代码,那就不用担心了!但如果您是在生产环境中编写生产代码,情况就不同了“不太可能发生”意味着在测试中不太可能发生。因此,我们假设它最终会发生在客户站点。@jameslarge sleep()用于单元测试,或用于演示并行性,而不是用于生产性代码。我们不想为了好玩而睡觉,我会编辑它