Java同步:synchronized,wait(),notify()

Cur*_*ind 8 java multithreading synchronization

我想了解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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

我的疑问是:

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

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

任何人都可以帮我澄清这些疑惑吗?

And*_*lko 5

它会立即释放锁吗?

不,不是。线程继续在同步块内执行下一个语句。

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

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

  • 如果Block的执行正常完成,则监视器将解锁,并且synced语句将正常完成。
  • 如果由于某种原因突然终止了该块的执行,则监视器将被解锁,并且由于相同的原因,同步语句也会突然完成。
    JLS-14.19

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

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

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


1.在的情况下notify,它是一个任意选择的线程。
2.或所有这些-如果通知的线程一直保持着监视器。