Эти потоки Java ожидают блокировки, которую он получает? - PullRequest
0 голосов
/ 26 июня 2018

Я смотрю журнал jstack, и вот что я вижу:

"com.mchange.v2.async.ThreadPoolAsynchronousRunner $ PoolThread- # 2" # 250 демон prio = 5 os_prio = 0 tid = 0x00007f9de0016000 nid = 0x7e54, работающий [0x00007f9d6495a000] java.lang.Thread.State: RUNNABLE в com.mchange.v2.async.ThreadPoolAsynchronousRunner $ PoolThread.run (ThreadPoolAsynchronousRunner.java:534) - заблокировано <<strong> 0x00000006fa818a38 > (com.mchange.v2.async.ThreadPoolAsynchronousRunner)

"com.mchange.v2.async.ThreadPoolAsynchronousRunner $ PoolThread- # 1" # 249 демон prio = 5 os_prio = 0 tid = 0x00007f9de000c000 nid = 0x7e53, ожидающий записи монитора [0x00007f9d649db000] java.lang.Thread.State: BLOCKED (на объектном мониторе) в java.lang.Object.wait (родной метод) - ожидание <<strong> 0x00000006fa818a38 > (com.mchange.v2.async.ThreadPoolAsynchronousRunner) в com.mchange.v2.async.ThreadPoolAsynchronousRunner $ PoolThread.run (ThreadPoolAsynchronousRunner.java:534) - заблокировано <<strong> 0x00000006fa818a38 > (com.mchange.v2.async.ThreadPoolAsynchronousRunner)

"com.mchange.v2.async.ThreadPoolAsynchronousRunner $ PoolThread- # 0" # 248 демон prio = 5 os_prio = 0 tid = 0x00007f9de001a000 nid = 0x7e52, ожидающий записи монитора [0x00007f9d64a5c000] java.lang.Thread.State: BLOCKED (на объектном мониторе) в java.lang.Object.wait (родной метод) - ожидание <<1020 *<em> 0x00000006fa818a38 > (com.mchange.v2.async.ThreadPoolAsynchronousRunner) в com.mchange.v2.async.ThreadPoolAsynchronousRunner $ PoolThread.run (ThreadPoolAsynchronousRunner.java:534) - заблокировано <<strong> 0x00000006fa818a38 > (com.mchange.v2.async.ThreadPoolAsynchronousRunner)

Итак, в этом журнале каждому из этих трех потоков удалось получить одинаковую блокировку, а два нижних потока фактически заблокированы в ожидании такой же блокировки.

Может кто-нибудь объяснить мне, что означает этот журнал стека?

Ответы [ 3 ]

0 голосов
/ 26 июня 2018

Последние два потока ожидают уведомления, используя экземпляр ThreadPoolAsynchronousRunner в качестве монитора, поэтому источник этого будет выглядеть примерно так:

synchronized(asyncRunner) {
    // ...
    asyncRunner.wait();
    // ...
}

Как только вы вызываете wait, синхронизация на asyncRunner «освобождается», то есть другие части приложения могут вводить блок, который синхронизируется в этом экземпляре. В вашем конкретном случае кажется, что это произошло, и был возвращен вызов wait первого потока, и в настоящее время он обрабатывает некоторые данные, которые поступают из него. Вы по-прежнему видите несколько locked -линий в дампе потока, чтобы показать вам, что код в настоящее время находится в synchronized -блоке, но, как уже было сказано, «блокировка» освобождается при вызове wait.

Техника, которую вы видите здесь как дамп потока, довольно распространена до того, как concurrent-пакет был добавлен в JDK, чтобы избежать дорогостоящих созданий потоков. И ваш поток-дамп выглядит как этот вид реализации. Вот простая реализация, как это может выглядеть "под капотом":

// class ThreadPoolAsynchronousRunner
private Deque<AsyncMessage> queue;

public synchronized void addAsyncMessage(AsyncMessage msg) {
    queue.add(msg);
    notifyAll();
}

public void start() {
    for (int i = 0; i < 4; i++) {
        PoolThread pt = new PoolThread(this);
        pt.start();
    }
}

ThreadPoolAsynchronousRunner`` запускает PoolThreads и выполняет notifyAll, если добавляется новое обрабатываемое сообщение.

// PoolThread

public PoolThread(ThreadPoolAsynchronousRunner parent) {
    this.parent = parent;
}

public void run() {
    try {
        while (true) {
            AsyncMessage  msg = null;
            synchronized(parent) {
                parent.wait();
                if (!parent.queue.isEmpty()) {
                    msg = queue.removeFirst();
                }
            }
            if (msg != null) {
                processMsg(msg);
            }
        }
    }
    catch(InterruptedException ie) {
        // exit
    }
}

notifyAll приведет к возврату всех wait -методов всех потоков, поэтому вы должны проверить, содержит ли очередь в родительском элементе данные (иногда wait возвращается даже без уведомления, поэтому вам нужно эта проверка, даже если не используется notifyAll). Если это так, вы запускаете метод обработки. Вы должны делать это за пределами synchronized -блока, иначе ваш класс асинхронной обработки обрабатывает только одно сообщение за раз (если только это не то, что вы хотите - но тогда зачем запускать несколько PoolThread -инстанций?)

0 голосов
/ 26 июня 2018

Только Thread- # 2 удалось успешно получить блокировку объекта, и она находится в состоянии RUNNABLE . Другие 2 потока, то есть Thread- # 0 и Thread- # 1 , ожидают снятия блокировки с помощью Thread- # 2 . Пока Thread- # 2 удерживает блокировку, Thread- # 0 и Thread- # 1 останутся заблокированными и будут в состоянии BLOCKED .

Если у вас есть доступ к исходному коду, вы можете просмотреть этот код только для того, чтобы убедиться, что блокировка и разблокировка выполнены в правильном порядке, и блокировка удерживалась только для той части кода, где это необходимо. Помните, что эти 2 потока находятся не в состоянии WAIT , а в состоянии BLOCKED , которое является шагом после состояния WAIT и всего лишь шагом до входа в RUNNABLE состояние, как только блокировка доступна.

В этом фрагменте журнала не наблюдается никаких проблем. Это еще не тупиковый сценарий.

0 голосов
/ 26 июня 2018

Что я могу видеть и понимать, так это

Thread- # 2 находится в состоянии Runnable и получил блокировку для объекта

"com.mchange.v2.async.ThreadPoolAsynchronousRunner $ PoolThread- # 2" java.lang.Thread.State: RUNNABLE

Thread- # 1 и Thread- # 0 ожидают снятия блокировки объекта и, следовательно, блокировки прямо сейчас.

"com.mchange.v2.async.ThreadPoolAsynchronousRunner $ PoolThread- # 1" "Com.mchange.v2.async.ThreadPoolAsynchronousRunner $ PoolThread- # 0" java.lang.Thread.State: BLOCKED (на объектном мониторе) в java.lang.Object.wait (собственный метод) - ожидает <0x00000006fa818a38>

...