bicho raro con la declaración de espera en el bloque sincronizado

mualloc:

Un extracto del archivo 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");
        }
        }
        }

Esto es de 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);
    }

}

Para una prueba con 4 hilos de trabajo, todo estaba bien antes de agregar la parte extractada en Worker.java. Luego, para informar de la acción de cada trabajador después de que todos los trabajadores se hacen, añadí esa parte. Algoritmo es muy simple. Cuando un trabajador termina su trabajo, se comprueba si hay algún trabajo en una TaskQueue y ProductOwner. Si no los hay, se rompe su bucle y luego disminuirá en 1 hilos de trabajadores activos de contador en el Maestro y luego llama a la espera en el campo AllFinished del Maestro. método run () de cheques PeriodicIncrement este contador y si es 0 (es decir, todos los trabajadores terminaron su puesto de trabajo), que llama notifyAll () en AllFinished.

El problema es que a veces uno dos hilos que están entrando en bloque de código extractada en Worker.java pero nunca entran restos subprocesos de trabajo de manera activa contador nunca se disminuye a 0 y mi programa nunca termina. Si tan sólo en comentario la parte extractada en Worker.java, excepto el acabado y la presentación de informes al azar, todo está bien. Lo que quiero decir es la parte extractada parece ser problemático.

¿Me puede ayudar a averiguar?

Usted dice:

Esa era una diversión de depurar, después de tanto tiempo sin jugar con las primitivas de concurrencia de bajo nivel. El truco a causa de origen se trataba de utilizar el jstack herramienta proporcionada por el 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

Algunas observaciones:

  • Hilo Timer-0(que es su tarea periódica) está en estado de bloqueo, a la espera de bloqueo 0x000000076bc70db8, que es una instancia deProgrammer
  • hay 4 programadores:
    • 2 de ellos todavía están haciendo algo de trabajo y están sosteniendo una cerradura de tipo Programmer(que en realidad está sosteniendo un bloqueo en sí mismos)
    • los otros 2 programadores se hacen y se mantienen dos cierres: uno sobre sí mismos, y uno en AllFinished. Programmer-3es un ejemplo de tal hilo.

Dado que los intentos de tareas periódicas para adquirir un bloqueo en Programmer-3antes de comunicarla, tiene que esperar a que Programmer-3para liberar el bloqueo en sí mismo, que no puede hacerlo porque es esperar a que todas las tareas para completar. ¡Punto muerto!

La razón por la cual los programadores están llevando a cabo un bloqueo en sí mismos es la siguiente:

public synchronized void work()

Esto se va a poner todo el workmétodo en un bloque sincronizado qué monitor pertenece a this. Dado que la Programmerclase no tiene estado y sobre todo hace un trabajo que no hace interactuar con otros hilos, en realidad se puede sincronizar una parte mucho más pequeña del workmétodo. Así que hay dos cambios que hacer:

  • eliminar synchronizedde la firmawork
  • sincronizar la llamada a waitdentro del workmétodo:

    synchronized (this) {
        wait();
    }
    

Una lección que se puede dar es que, cuando se utiliza bloque sincronizado, siempre se desea sincronizar el menor código posible. Todo lo que no necesita ser sincronizado debe estar fuera del bloque, con el fin de maximizar el paralelismo (todo lo que sucede en el bloque es secuencial), lo que podría requerir de la cerradura con menos frecuencia (puede haber condiciones que le permiten omitir la adquisición de bloqueo si lo pones en el nivel más bajo, por lo que reducirá la sobrecarga de sincronización), y en algunos casos como éste, evitando estancamientos.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=370985&siteId=1
Recomendado
Clasificación