同期ブロックでのwait文との奇妙なバグ

mualloc:

Worker.javaファイルからの抜粋:

public class Worker extends Thread{

        public void run(){
                // Worker Thread periodically does its job.

       Master.getInstance().decrementNumOfWorkingWorkers();
        // This is the reporting part of the thread.
        // Aimed to wait other threads finish their job.
                synchronized (Master.getInstance().allFinished) {
            while (  Master.getInstance().getNumOfWorkingWorkers() > 0) {
                try {
                    Master.getInstance().allFinished.wait();
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            Main.printSync("Worker Thread-" + getPId() + " worked on");
        }
        }
        }

これはMaster.Javaからです。

import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;

public class Master extends Timer {

    AllFinished allFinished;
    int day;
    public TimerTask task;
    LinkedList<Worker> Workers;
    private static Master instance = null;
    int numOfWorkingWorkers = 0;

    public class AllFinished
    {

    }

    public class PeriodicIncrement extends TimerTask {
        // Complete this class

        public void run() {

            Main.printSync("Day " + day + ":");
            Main.printSync("Queue: " + TaskQueue.getInstance().ConvertToString());

            day++;
            for (int i = 0; i < Workers.size(); i++) {

                synchronized (Workers.get(i)) {
                    Workers.get(i).notify();
                }
            }

            if (0 == numOfWorkingWorkers) {

                synchronized (allFinished) {
                    allFinished.notifyAll();
                }
                cancel(); // Terminate the timer thread
            }
        }
    }

    private Master(LinkedList<Worker> Workers) {
        super();
        this.task = new PeriodicIncrement();
        day = 0;
        allFinished = new AllFinished();
        this.Workers = Workers;
        numOfWorkingWorkers = this.Workers.size();
        this.schedule(task, 100, 100);
    }

}

私はWorker.javaで抜粋部分を追加する前に4ワーカースレッドでのテストでは、すべてが大丈夫でした。そして、全ての労働者が行われた後、各ワーカーの行動を報告するために、私はその部分を追加しました。アルゴリズムは非常に簡単です。労働者がその仕事を終えるとタスクキューとProductOwnerでどんな仕事があるかどうかをチェックします。どんな存在していない場合は、そのループを破り、その後1つのアクティブワーカースレッドがマスターに対抗して、マスターのAllFinishedフィールド上で待機を呼び出すことにより、デクリメント。PeriodicIncrementチェックこのカウンタの()メソッドを実行し、それが0であれば(すべての労働者が自分の仕事を終え意味する)、それはAllFinished上のnotifyAll()を呼び出します。

問題は、人は時々2つのスレッドがWorker.javaでその抜粋コードブロックを入力しているされているが、remainingsはとてもアクティブなワーカースレッドカウント0までデクリメント決して、私のプログラムが終了することはありませんを入力することはありません。私はちょうど仕上げとランダムに報告を除き、Worker.javaに抜粋部分をコメントアウトした場合、すべてが正常です。私は何を意味することは抜粋部分が問題のようですです。

あなたは見つけるために私を助けてもらえますか?

あなたは言います:

それは後に多くの時間が低レベルの並行処理プリミティブで遊んでいない、デバッグに楽しいものでした。根本原因へのトリックは、これは使用していたjstack JDKが提供するツールを。

╭───courtino ~
╰➤  sudo jstack -l 63978
2020-03-29 21:26:01
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.231-b11 mixed mode):

"DestroyJavaVM" #18 prio=5 os_prio=31 tid=0x00007ffa91491000 nid=0x1803 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"Timer-0" #17 prio=5 os_prio=31 tid=0x00007ffa91f36800 nid=0x5903 waiting for monitor entry [0x0000700009aa4000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.amazon.adnumsmissionmanagerservice.homework.ScrumMaster$PeriodicIncrement.run(ScrumMaster.java:42)
- waiting to lock <0x000000076bc70db8> (a com.amazon.adnumsmissionmanagerservice.homework.Programmer)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)

"Programmer-4" #15 prio=5 os_prio=31 tid=0x00007ffa92cc6800 nid=0x5603 in Object.wait() [0x000070000989e000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.amazon.adnumsmissionmanagerservice.homework.Programmer.completeTasksUntilNoneAvailable(Programmer.java:230)
at com.amazon.adnumsmissionmanagerservice.homework.Programmer.work(Programmer.java:165)
- locked <0x000000076bc71a58> (a com.amazon.adnumsmissionmanagerservice.homework.Programmer)
at com.amazon.adnumsmissionmanagerservice.homework.Programmer.run(Programmer.java:241)

"Programmer-3" #14 prio=5 os_prio=31 tid=0x00007ffa923af800 nid=0x5503 in Object.wait() [0x000070000979b000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.amazon.adnumsmissionmanagerservice.homework.Programmer.work(Programmer.java:176)
- locked <0x000000076bcc8ac0> (a com.amazon.adnumsmissionmanagerservice.homework.ScrumMaster$AllFinished)
- locked <0x000000076bc70db8> (a com.amazon.adnumsmissionmanagerservice.homework.Programmer)
at com.amazon.adnumsmissionmanagerservice.homework.Programmer.run(Programmer.java:241)

"Programmer-2" #13 prio=5 os_prio=31 tid=0x00007ffa92c25000 nid=0x3f03 in Object.wait() [0x0000700009698000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.amazon.adnumsmissionmanagerservice.homework.Programmer.work(Programmer.java:176)
- locked <0x000000076bcc8ac0> (a com.amazon.adnumsmissionmanagerservice.homework.ScrumMaster$AllFinished)
- locked <0x000000076bc5fca0> (a com.amazon.adnumsmissionmanagerservice.homework.Programmer)
at com.amazon.adnumsmissionmanagerservice.homework.Programmer.run(Programmer.java:241)

"Programmer-1" #12 prio=5 os_prio=31 tid=0x00007ffa91fab800 nid=0x4203 in Object.wait() [0x0000700009595000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.amazon.adnumsmissionmanagerservice.homework.Programmer.completeTasksUntilNoneAvailable(Programmer.java:230)
at com.amazon.adnumsmissionmanagerservice.homework.Programmer.work(Programmer.java:165)
- locked <0x000000076bc43c80> (a com.amazon.adnumsmissionmanagerservice.homework.Programmer)
at com.amazon.adnumsmissionmanagerservice.homework.Programmer.run(Programmer.java:241)

"Service Thread" #11 daemon prio=9 os_prio=31 tid=0x00007ffa91f30800 nid=0x4403 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"C1 CompilerThread2" #10 daemon prio=9 os_prio=31 tid=0x00007ffa9227c000 nid=0x3c03 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #9 daemon prio=9 os_prio=31 tid=0x00007ffa9227b000 nid=0x4603 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #8 daemon prio=9 os_prio=31 tid=0x00007ffa92272800 nid=0x4803 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"JDWP Command Reader" #7 daemon prio=10 os_prio=31 tid=0x00007ffa9200f000 nid=0x3a03 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"JDWP Event Helper Thread" #6 daemon prio=10 os_prio=31 tid=0x00007ffa91019800 nid=0x4a03 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"JDWP Transport Listener: dt_socket" #5 daemon prio=10 os_prio=31 tid=0x00007ffa9181a000 nid=0x4b07 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007ffa9180d800 nid=0x3603 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007ffa91002000 nid=0x3003 in Object.wait() [0x0000700008b77000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076ab08ed8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x000000076ab08ed8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007ffa92006800 nid=0x2e03 in Object.wait() [0x0000700008a74000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076ab06c00> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076ab06c00> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=31 tid=0x00007ffa90843000 nid=0x2d03 runnable

"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007ffa91001800 nid=0x2307 runnable

"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007ffa91801800 nid=0x2a03 runnable

"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007ffa91802000 nid=0x5303 runnable

"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007ffa91802800 nid=0x5203 runnable

"VM Periodic Task Thread" os_prio=31 tid=0x00007ffa91357800 nid=0x3d03 waiting on condition

JNI global references: 2236

いくつかの所見:

  • スレッドTimer-0ロックを待って、(あなたの定期的なタスクをだ)BLOCKED状態にある0x000000076bc70db8のインスタンスであり、Programmer
  • 4人のプログラマがあります。
    • それらの2には、まだいくつかの作業を行っていると、タイプの1つのロックを保持しているProgrammer(彼らは実際に自分自身のロックを保持しています)
    • 他の2人のプログラマが行われ、2つのロック保持されています。自分自身の1、および1つ上をAllFinishedProgrammer-3このようなスレッドの一例です。

定期的なタスクは上のロックを取得しようとしているのでProgrammer-3、それを通知する前に、それは待たなければならないProgrammer-3、それは完全にすべてのタスクを待っていますので、それを行うことができない、自分自身のロックを解除します。デッドロック!

あなたのプログラマが自分自身のロックを保持している理由はこれです:

public synchronized void work()

これは、全体置くために起こっているworkモニターが属する同期ブロックに方法をthis以来Programmerクラスはステートレスであり、それは主に他のスレッドと相互作用しない仕事をして、あなたが実際のはるかに小さい部分同期することができるwork方法を。だから、作るために2つの変更を持っています:

  • 削除synchronizedの署名からwork
  • 呼び出しを同期するwait内部work方法:

    synchronized (this) {
        wait();
    }
    

それが与えることができるという一つの教訓は、synchronizedブロックを使用しているとき、あなたは常に可能な限り小さなコードとして同期させたい、ということです。(あなたはスキップすることができる条件があるかもしれない潜在的に少なく、多くの場合、ロックを必要とするため、(ブロックで起こるすべてがシーケンシャルである)並列性を最大にするために、外側のブロックのあるべき同期する必要はありません。何もそれがデッドロックを避け、)同期のオーバーヘッドを削減し、このようないくつかのケースでますので、あなたが、最も低いレベルでそれを置けば、ロック獲得。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=370982&siteId=1
おすすめ