为什么Java 多线程代码死锁
Why does Java muti-threaded code deadlock
此问题的完整代码可在此处获得:
https://github.com/NACHC-CAD/thread-tool
下面显示的代码似乎 运行 完成但从未逃脱此处显示的 while 循环。
while (this.active.size() > 0) {
// System.out.println("here");
}
如果我取消对 // System.out.println("here");
的注释,它会 运行 完成。如果我添加 sleep-for-one-second,它也会运行完成。
更大的问题是,如果我尝试在实际应用程序中使用此代码,代码会运行一段时间然后似乎死锁(即代码只是停止 运行)。
我需要做什么来解决这个问题?
package org.nachc.tools.threadtool;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.nachc.tools.threadtool.runnableiter.ThreadToolUser;
import org.nachc.tools.threadtool.worker.ThreadToolWorker;
import org.nachc.tools.threadtool.worker.ThreadToolWorkerRunnable;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ThreadRunner {
private int numberOfThreadsPerWorker;
private int numberOfRunnablesPerWorker;
private int numberOfWorkers;
private ThreadToolUser runnableIter;
private List<ThreadToolWorker> active = new ArrayList<ThreadToolWorker>();
private Object lock = new Object();
private ThreadPoolExecutor executor;
public ThreadRunner(int numberOfThreadsPerWorker, int numberOfRunnablesPerWorker, int numberOfWorkers, ThreadToolUser runnableIter) {
this.numberOfThreadsPerWorker = numberOfThreadsPerWorker;
this.numberOfRunnablesPerWorker = numberOfRunnablesPerWorker;
this.numberOfWorkers = numberOfWorkers;
this.runnableIter = runnableIter;
this.executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(numberOfWorkers);
}
public void exec() {
synchronized (lock) {
addWorkers();
}
while (this.active.size() > 0) {
// System.out.println("here");
}
log.info("SHUTTING DOWN----------------");
executor.shutdown();
try {
executor.awaitTermination(1000, TimeUnit.HOURS);
} catch (Exception exp) {
throw (new RuntimeException(exp));
}
}
private void addWorkers() {
log.info("start addWorkers");
while (runnableIter.hasNext() && active.size() < numberOfWorkers) {
ThreadToolWorker worker = getNextWorker();
if (worker == null) {
break;
} else {
this.active.add(worker);
}
}
log.info("done addWorkers");
}
private ThreadToolWorker getNextWorker() {
synchronized (lock) {
log.info("start next worker");
if (runnableIter.hasNext() == false) {
return null;
}
List<Runnable> runnableList = new ArrayList<Runnable>();
while (runnableList.size() < numberOfRunnablesPerWorker && runnableIter.hasNext()) {
runnableList.add(runnableIter.getNext());
}
ThreadToolWorker worker = new ThreadToolWorker(runnableList, numberOfThreadsPerWorker, this);
ThreadToolWorkerRunnable runnable = new ThreadToolWorkerRunnable(worker);
this.executor.execute(runnable);
log.info("done next worker");
return worker;
}
}
public void done(ThreadToolWorker worker) {
synchronized (lock) {
log.info("start done");
this.active.remove(worker);
if (active.size() > 0) {
addWorkers();
}
log.info("done done");
}
}
public void logActive() {
synchronized (lock) {
log.info("------------");
log.info("active: " + active.size());
log.info("waiting: " + runnableIter.waiting());
log.info("------------");
}
}
}
您的循环在没有任何同步的情况下访问 this.active.size()
。 Java 的 memory visibility rules 不保证何时(如果 ever)一个查看共享变量的线程将看到其他线程所做的更改,如果正在查看的线程确实不与其他线程使用某种形式的同步。
如果你想 poll active
列表的状态,那么考虑做这样的事情:
while (true) {
synchronized(lock) {
if (this.active.size() <= 0) break;
}
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
}
sleep()
调用让其他线程有机会 运行。如果没有睡眠,运行 执行此循环的线程将使用 100% 的 CPU,这可能会损害应用程序的性能。
但是还有比轮询更好的选择。您可以执行此操作以等待 active
列表变空。
synchronized(lock) {
while (this.active.size() > 0) {
lock.wait();
}
}
wait()
call temporarily releases the lock
, then it waits for another thread to notify()
锁,最后 re-locks 它之前的锁 returns:
public void done(ThreadToolWorker worker) {
synchronized (lock) {
log.info("start done");
this.active.remove(worker);
if (active.size() > 0) {
addWorkers();
}
else {
lock.notifyAll();
}
log.info("done done");
}
}
此问题的完整代码可在此处获得: https://github.com/NACHC-CAD/thread-tool
下面显示的代码似乎 运行 完成但从未逃脱此处显示的 while 循环。
while (this.active.size() > 0) {
// System.out.println("here");
}
如果我取消对 // System.out.println("here");
的注释,它会 运行 完成。如果我添加 sleep-for-one-second,它也会运行完成。
更大的问题是,如果我尝试在实际应用程序中使用此代码,代码会运行一段时间然后似乎死锁(即代码只是停止 运行)。
我需要做什么来解决这个问题?
package org.nachc.tools.threadtool;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.nachc.tools.threadtool.runnableiter.ThreadToolUser;
import org.nachc.tools.threadtool.worker.ThreadToolWorker;
import org.nachc.tools.threadtool.worker.ThreadToolWorkerRunnable;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ThreadRunner {
private int numberOfThreadsPerWorker;
private int numberOfRunnablesPerWorker;
private int numberOfWorkers;
private ThreadToolUser runnableIter;
private List<ThreadToolWorker> active = new ArrayList<ThreadToolWorker>();
private Object lock = new Object();
private ThreadPoolExecutor executor;
public ThreadRunner(int numberOfThreadsPerWorker, int numberOfRunnablesPerWorker, int numberOfWorkers, ThreadToolUser runnableIter) {
this.numberOfThreadsPerWorker = numberOfThreadsPerWorker;
this.numberOfRunnablesPerWorker = numberOfRunnablesPerWorker;
this.numberOfWorkers = numberOfWorkers;
this.runnableIter = runnableIter;
this.executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(numberOfWorkers);
}
public void exec() {
synchronized (lock) {
addWorkers();
}
while (this.active.size() > 0) {
// System.out.println("here");
}
log.info("SHUTTING DOWN----------------");
executor.shutdown();
try {
executor.awaitTermination(1000, TimeUnit.HOURS);
} catch (Exception exp) {
throw (new RuntimeException(exp));
}
}
private void addWorkers() {
log.info("start addWorkers");
while (runnableIter.hasNext() && active.size() < numberOfWorkers) {
ThreadToolWorker worker = getNextWorker();
if (worker == null) {
break;
} else {
this.active.add(worker);
}
}
log.info("done addWorkers");
}
private ThreadToolWorker getNextWorker() {
synchronized (lock) {
log.info("start next worker");
if (runnableIter.hasNext() == false) {
return null;
}
List<Runnable> runnableList = new ArrayList<Runnable>();
while (runnableList.size() < numberOfRunnablesPerWorker && runnableIter.hasNext()) {
runnableList.add(runnableIter.getNext());
}
ThreadToolWorker worker = new ThreadToolWorker(runnableList, numberOfThreadsPerWorker, this);
ThreadToolWorkerRunnable runnable = new ThreadToolWorkerRunnable(worker);
this.executor.execute(runnable);
log.info("done next worker");
return worker;
}
}
public void done(ThreadToolWorker worker) {
synchronized (lock) {
log.info("start done");
this.active.remove(worker);
if (active.size() > 0) {
addWorkers();
}
log.info("done done");
}
}
public void logActive() {
synchronized (lock) {
log.info("------------");
log.info("active: " + active.size());
log.info("waiting: " + runnableIter.waiting());
log.info("------------");
}
}
}
您的循环在没有任何同步的情况下访问 this.active.size()
。 Java 的 memory visibility rules 不保证何时(如果 ever)一个查看共享变量的线程将看到其他线程所做的更改,如果正在查看的线程确实不与其他线程使用某种形式的同步。
如果你想 poll active
列表的状态,那么考虑做这样的事情:
while (true) {
synchronized(lock) {
if (this.active.size() <= 0) break;
}
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
}
sleep()
调用让其他线程有机会 运行。如果没有睡眠,运行 执行此循环的线程将使用 100% 的 CPU,这可能会损害应用程序的性能。
但是还有比轮询更好的选择。您可以执行此操作以等待 active
列表变空。
synchronized(lock) {
while (this.active.size() > 0) {
lock.wait();
}
}
wait()
call temporarily releases the lock
, then it waits for another thread to notify()
锁,最后 re-locks 它之前的锁 returns:
public void done(ThreadToolWorker worker) {
synchronized (lock) {
log.info("start done");
this.active.remove(worker);
if (active.size() > 0) {
addWorkers();
}
else {
lock.notifyAll();
}
log.info("done done");
}
}