Java Thread等待和通知方法

时间:2015-09-15 16:49:25

标签: 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();
        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();
                }
            }
        }
    }
}

在此示例中,控制台输出为I've entered in FirstThread,因为第一个线程启动,调用notify()方法,然后第二个线程启动,调用wait()方法,字符串“我是在第二个帖子中“没有打印。

下一个场景是我反转new Main().new FirstThread().start();new Main().new SecondThread().start();的位置

I've entered in FirstThread
I'm in the second thread

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

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

我的最后一个问题是,为什么锁定对象必须是静态的,因为如果我删除静态修饰符,输出总是I've entered in FirstThread

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

3 个答案:

答案 0 :(得分:3)

线程是按顺序启动的,理论上线程1会在线程2之前执行,虽然它不能保证(非常确定它在这个简单的情况下会保持一致,因为没有真正的或模拟的随意延迟)。

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

staticObject上:您将[First/Second]Thread嵌套类绑定到Main实例,因此锁必须如果您希望它们在同一个锁上同步,则两者都是通用的。

如果它是一个实例对象,您的线程将访问并同步不同的锁定,因为new Main()...成语将获得两个Main实例,随后是两个lock实例。

答案 1 :(得分:3)

  

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

是的,您可以使用随机时间引入sleep(),以获得更好的(单元)测试或演示目的。 (当然,最终运行的代码不应该有睡眠)

  

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

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

答案 2 :(得分:0)

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

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()调用将不执行任何操作。

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

“共享状态”可以像单个布尔一样简单:

 boolean firstThreadRan = false;

通知人(a.k.a。,“生产者”)这样做:

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

服务员(a.k.a。,“消费者”)这样做:

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

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