我想我做错了。我正在创建线程,假设从共享队列中处理一些数据。我的问题是程序很慢并且内存耗尽,我怀疑队列可能不像我希望的那样共享。我怀疑这是因为在我的代码中我添加了一行显示队列的大小,如果我启动2个线程,那么我得到两个输出具有完全不同的数字并且似乎自己递增(我认为它可能是相同的数字但是也许是从100跳到2,依此类推,但是在观看后显示105和5并以不同的速度进行。如果我有4个线程,那么我会看到4个不同的数字。)
以下是相关部分的摘要。我在程序顶部的队列中创建了一个包含我想要的数据的静态类
static class queue_class {
int number;
int[] data;
Context(int number, int[] data) {
this.number = number;
this.data = data;
}
}
然后我在将一些作业发送到callable后创建队列..
static class process_threaded implements Callable<Void> {
// queue with contexts to process
private Queue<queue_class> queue;
process_threaded(queue_class request) {
queue = new ArrayDeque<queue_class>();
queue.add(request);
}
public Void call() {
while(!queue.isEmpty()) {
System.out.println("in contexts queue with a size of " + queue.size());
Context current = contexts.poll();
//get work and process it, if it work great then the solution goes elsewhere
//otherwise, depending on the data, its either discarded or parts of it is added back to queue
queue.add(new queue_class(k, data_list));
正如您所看到的,数据有3个选项,如果数据良好则被发送,如果数据完全可怕则被丢弃或被发送回队列。我认为队列在被发送回来的时候会发生,但我怀疑是因为每个线程都在自己的队列中而不是共享队列。
这个猜测是否正确,我做错了吗?
答案 0 :(得分:2)
您的评估中正确的是,每个线程(可能)都在使用自己的队列,因为您正在Callable
的构造函数中创建队列。 (拥有Callable<Void>
实际上非常奇怪 - 不仅仅是Runnable
吗?)
还有其他问题,例如,您正在使用非线程安全的队列,或者您的代码在编写时无法编译。
但重要的问题是,真的是否需要首先显式创建队列?为什么没有ExecutorService
向其提交Callable
(或Runnables
,如果您决定进行切换):将对执行者的引用传递到Callable
s ,他们可以将新的Callable
添加到执行程序的任务队列中来运行。无需重新发明轮子。
例如:
static class process_threaded implements Runnable {
// Reference to an executor
private final ExecutorService exec;
// Reference to the job counter
private final AtomicInteger jobCounter;
// Request to process
private queue_class request;
process_threaded( ExecutorService exec, AtomicInteger counter, queue_class request) {
this.exec = exec;
this.jobCounter = counter;
this.jobCounter.incrementAndGet(); // Assuming that you will always
// submit the process_threaded to
// the executor if you create it.
this.request = request;
}
public run() {
//get work and process **request**, if it work great then the solution goes elsewhere
//otherwise, depending on the data, its either discarded or parts of are added back to the executor
exec.submit( new process_threaded( exec, new queue_class(k, data_list) ) );
// Can do some more work
// Always run before returning: counter update and notify the launcher
synchronized(jobCounter){
jobCounter.decrementAndGet();
jobCounter.notifyAll();
}
}
}
修改强>
为了解决何时关闭执行程序的问题,我认为最简单的解决方案是使用作业计数器,并在达到0时关闭。对于线程安全,AtomicInteger
可能是最佳选择。我在上面添加了一些代码来合并更改。然后你的启动代码看起来像这样:
void theLauncher() {
AtomicInteger jobCounter = new AtomicInteger( 0 );
ExecutorService exec = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcesses());
exec.submit( new process_threaded( exec, jobCounter, someProcessRequest ) );
// Can submit some other things here of course...
// Wait for jobs to complete:
for(;;jobCounter.get() > 0){
synchronized( jobCounter ){ // (I'm not sure if you have to have the synchronized block, but I think this is safer.
if( jobCounter.get() > 0 )
jobCounter.wait();
}
}
// Now you can shutdown:
exec.shutdown();
}
答案 1 :(得分:2)
不要重新发明轮子!如何使用ConcurrentLinkedQueue?来自javadocs:
基于链接节点的无界线程安全队列。此队列对元素FIFO(先进先出)进行排序。队列的头部是队列中最长时间的元素。队列的尾部是队列中最短时间的元素。新元素插入队列的尾部,队列检索操作获取队列头部的元素。当许多线程共享对公共集合的访问权时,ConcurrentLinkedQueue是一个合适的选择。