我有以下工作队列实现,我用它来限制使用的线程数。我最初在队列中添加了许多Runnable对象,当我准备开始时,我运行“begin()”。此时我不再添加任何队列。
com.android.support:design:23.0.1
这是一个可运行的,用于跟踪工作队列中的所有作业何时完成:
public class WorkQueue {
private final int nThreads;
private final PoolWorker[] threads;
private final LinkedList queue;
Integer runCounter;
boolean hasBegun;
public WorkQueue(int nThreads) {
runCounter = 0;
this.nThreads = nThreads;
queue = new LinkedList();
threads = new PoolWorker[nThreads];
hasBegun = false;
for (int i = 0; i < nThreads; i++) {
threads[i] = new PoolWorker();
threads[i].start();
}
}
public boolean isQueueEmpty() {
synchronized (queue) {
if (queue.isEmpty() && runCounter == 0) {
return true;
} else {
return false;
}
}
}
public void begin() {
hasBegun = true;
synchronized (queue) {
queue.notify();
}
}
public void add(Runnable r) {
if (!hasBegun) {
synchronized (queue) {
queue.addLast(r);
runCounter++;
}
} else {
System.out.println("has begun executing. Cannot add more jobs ");
}
}
private class PoolWorker extends Thread {
public void run() {
Runnable r;
while (true) {
synchronized (queue) {
while (queue.isEmpty()) {
try {
queue.wait();
} catch (InterruptedException ignored) {
}
}
r = (Runnable) queue.removeFirst();
}
// If we don't catch RuntimeException,
// the pool could leak threads
try {
r.run();
synchronized (runCounter) {
runCounter--;
}
} catch (RuntimeException e) {
// You might want to log something here
}
}
}
}
}
这就是我使用它们的方式:
public class QueueWatcher implements Runnable {
private Thread t;
private String threadName;
private WorkQueue wq;
public QueueWatcher(WorkQueue wq) {
this.threadName = "QueueWatcher";
this.wq = wq;
}
@Override
public void run() {
while (true) {
if (wq.isQueueEmpty()) {
java.util.Date date = new java.util.Date();
System.out.println("Finishing and quiting at:" + date.toString());
System.exit(0);
break;
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(PlaneGenerator.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
public void start() {
wq.begin();
System.out.println("Starting " + threadName);
if (t == null) {
t = new Thread(this, threadName);
t.setDaemon(false);
t.start();
}
}
}
但无论我使用多少线程,结果总是相同的 - 它总是需要大约1毫秒才能完成。这与我刚刚执行单线程版本时(当所有内容都在main()中运行时)大致相同。 如果我将wq设置为(1,2,3--9)线程,它总是在1m8s-1m10s之间。问题是什么 ?作业(someRunnable)彼此无关,不能相互阻挡。
编辑:每个runnable只读取文件系统中的一些图像文件,并在单独的目录中创建新文件。新目录最终包含大约400个输出文件。
编辑:似乎只有一个线程始终在工作。我做了以下更改:我让Woolworker存储Id
Workqueue wq = new WorkQueue(9); //Get same results regardless of 1,2,3,8,9
QueueWatcher qw = new QueueWatcher(wq);
SomeRunnable1 sm1 = new SomeRunnable1();
SomeRunnable2 sm2 = new SomeRunnable2();
SomeRunnable3 sm3 = new SomeRunnable3();
SomeRunnable4 sm4 = new SomeRunnable4();
SomeRunnable5 sm5 = new SomeRunnable5();
wq.add(sm1);
wq.add(sm2);
wq.add(sm3);
wq.add(sm4);
wq.add(sm5);
qw.start();
在跑步之前,我打印了工人的身份。
PoolWorker(int id){
this.threadId = id;
}
在创建池工作者时,在WorkQueue构造函数中我做了:
System.out.println(this.threadId + " got new task");
r.run();
但似乎只有线程0才有效,因为输出总是:
0获得新任务
答案 0 :(得分:6)
使用queue.notifyAll()
开始处理。
目前您正在使用queue.notify()
,它只会唤醒一个线程。 (指出这一点的一个重要线索是当你提到只有一个线程正在运行时。)
此外,Integer runCounter
上的同步没有按照您的想法执行 - runCounter++
实际上每次都为Integer
分配一个新值,因此您正在同步很多不同的Integer
个对象。
另一方面,即使对于最优秀的程序员来说,使用原始线程和wait/notify
范例也很复杂且容易出错 - 这就是为什么Java引入了java.util.concurrent
package,它提供了线程安全的BlockingQueue
实现和Executors
用于轻松管理多线程应用程序。