【问题标题】:Java synchronization: synchronized, wait(), notify()Java 同步:同步、wait()、notify()
【发布时间】:2018-02-13 12:37:50
【问题描述】:

我正在尝试理解 Java 中的线程间通信,并阅读到支持来自使用:wait()notify()notifyAll() 方法。

为了让线程执行这些方法中的任何一个,线程需要是对象的lock 的所有者,线程正在为其调用(任何这些)方法。除此之外,所有这些方法都需要在synchronized 块/方法中。到目前为止还不错。

我试图实现一个程序,其中一个线程打印奇数,而另一个线程打印偶数。

程序运行正常,但同时也引发了一些疑问。

下面是我实现的程序的完整源代码。

PrintEvenNumThread.java // 打印偶数

package com.example.multithr.implrun;

import com.example.common.ObjectToWaitOn;

public class PrintEvenNumThread implements Runnable {

    private ObjectToWaitOn objectToWaitOn;

    public PrintEvenNumThread(ObjectToWaitOn objectToWaitOn) {
        this.objectToWaitOn = objectToWaitOn;
    }

    @Override
    public void run() {

    int numToPrint = 2;

    for (;;) {
        synchronized (objectToWaitOn) {
            while(objectToWaitOn.getPrintEvenOrOdd() != 2) {
                try {
                    objectToWaitOn.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }   
            }
                objectToWaitOn.print("EvenThread",numToPrint);
                numToPrint += 2; // Generate next even number
                objectToWaitOn.setPrintEvenOrOdd(1);
                objectToWaitOn.notifyAll();
            }
        }
    }
}

PrintOddNumsThread.java // 打印奇数

package com.example.multithr.implrun;

import com.example.common.ObjectToWaitOn;

public class PrintOddNumsThread implements Runnable {

    private ObjectToWaitOn objectToWaitOn;

    public PrintOddNumsThread(ObjectToWaitOn objectToWaitOn) {
        this.objectToWaitOn = objectToWaitOn;
    }

    @Override
    public void run() {
        int numToPrint = 1;

        for(;;) {

            synchronized(objectToWaitOn) {

                while(objectToWaitOn.getPrintEvenOrOdd() != 1) {  
                    try {
                        objectToWaitOn.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                objectToWaitOn.print("OddThread", numToPrint);
                numToPrint += 2; // Generate next odd number
                objectToWaitOn.setPrintEvenOrOdd(2);
                objectToWaitOn.notifyAll();
            }
        }
    }
}

ObjectToWaitOn.java // 线程间通信的“共享”对象

package com.vipin.common;

public class ObjectToWaitOn {

    private int printEvenOrOdd;

    public ObjectToWaitOn(int printEvenOrOdd) {
        this.printEvenOrOdd = printEvenOrOdd;
    }

    public int getPrintEvenOrOdd() {
        return printEvenOrOdd;
    }

    public void setPrintEvenOrOdd(int printEvenOrOdd) {
        this.printEvenOrOdd = printEvenOrOdd;
    }

    public void print(String byThread, int numToPrint) {
        System.out.println(byThread + ": " +numToPrint);
    }
}

PrintEvenOddNumsMainApp.java

package com.example.multithr.main.app1;

import com.example.common.ObjectToWaitOn;
import com.example.multithr.implrun.PrintEvenNumThread;
import com.example.multithr.implrun.PrintOddNumsThread;

    public class PrintEvenOddNumsMainApp {

        public static void main(String[] args) {

            ObjectToWaitOn obj = new ObjectToWaitOn(1); // 1 == odd; 2 == even

            PrintEvenNumThread printEvenNumThread = new PrintEvenNumThread(obj);
            PrintOddNumsThread printOddNumsThread = new PrintOddNumsThread(obj);

            Thread evenNum = new Thread(printEvenNumThread);
            Thread oddNum = new Thread(printOddNumsThread);

            evenNum.start();
            oddNum.start();
        }
    }

我的疑问是:

1) 当这些线程中的任何一个通过在对象objectToWaitOn(这些线程之间共享)上调用notifyAll() 来释放锁时,它会立即释放锁吗?我有这个疑问是因为这些线程位于基于objectToWaitOn 对象的synchronized 块中;所以即使一个线程调用了 notifyAll(),它不应该因为它在同步块中而仍然持有锁

2) 当一个线程通过在objectToWaitOn 上调用wait() 处于等待状态,并且如果其他线程通过调用notifyAll() 释放了锁,等待线程等待释放还是别的什么?无论如何,从synchronized 块中出来的线程不会释放它所持有的对象上的锁吗?所以在上面的例子中,如果一个线程在objectToWaitOn 上持有锁并且从synchronized 块中出来,它是否无论如何都不会释放objectToWaitOn 的锁,并且不应该基于此唤醒另一个线程?

谁能帮我澄清这些疑惑?

【问题讨论】:

    标签: java multithreading synchronization


    【解决方案1】:

    它会立即释放锁吗?

    不,它没有。线程继续执行同步块中的下一条语句。

    它不应该因为它在同步块中而仍然持有锁吗?

    是的,应该。调用notify/notifyAll方法的线程必须持有锁,并且会继续持有,直到它正常离开同步块或发生异常:

    • 如果块的执行正常完成,则监视器解锁,同步语句正常完成。
    • 如果块的执行出于任何原因突然完成,则监视器被解锁并且同步语句出于同样的原因突然完成。
      JLS-14.19

    notify/notifyAll 方法将在此监视器上等待的线程1 的状态从 State.WAITING 更改为 State.RUNNABLE。当线程被唤醒时,它们可以参与获取锁。

    来到监视器,其中一些2 可能会获得STATE.BLOCKED 状态并等待直到另一个线程释放锁。请注意,它不需要来自持有锁的线程的任何通知。

    在当前线程放弃对该对象的锁定之前,被唤醒的线程将无法继续。被唤醒的线程将以通常的方式与任何其他可能正在积极竞争同步的线程竞争在这个物体上;例如,被唤醒的线程在成为下一个锁定该对象的线程时享有没有可靠的特权或劣势
    docs


    1.notify的情况下,它是一个任意选择的线程。
    2. 或者全部 - 如果通知的线程一直持有监视器。

    【讨论】:

    • 如果没有 notifyAll() 并且线程退出同步块会发生什么?当线程从同步块中出来时,锁不会被释放吗?是否有必要等待等待()的线程需要通过 notify() 由其他线程通知,即使锁可能被持有线程释放(通过退出同步块并且没有任何 notify())
    • @CuriousMind 锁将被释放,但未通知的等待线程不会切换到运行状态(它们停留在State.WAITING)。您可以使用其中一种超时版本,例如wait​(long timeout, int nanos) 确保等待的线程不会永远等待。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-19
    • 2014-02-21
    • 2012-05-01
    • 2019-02-19
    • 1970-01-01
    相关资源
    最近更新 更多