我喜欢ExecutorService系列的类/接口。我不必担心线程;我接受一个ExecutorService
实例并使用它来安排任务,如果我想使用8线程或16线程池,那么,很好,我根本不用担心,它只是发生取决于ExecutorService的设置方式。乌拉!
但如果我的某些任务需要按顺序执行,我该怎么办?理想情况下,我会要求ExecutorService让我在一个线程上安排这些任务,但似乎没有任何方法可以这样做。
编辑:任务未提前知道,它们是由各种事件不规律地生成的无限系列任务(想想随机/未知到达过程:例如盖革的点击计数器或击键事件)。
答案 0 :(得分:6)
您可以编写Runnable
的实现来执行某些任务并以串行方式执行它们。
类似的东西:
public class SerialRunner implements Runnable {
private List<Runnable> tasks;
public SerialRunner(List<Runnable> tasks) {
this.tasks = tasks;
}
public void run() {
for (Runnable task: tasks) {
task.run();
}
}
}
答案 1 :(得分:2)
我正在使用使用Executors.newSingleThreadExecutor()创建的单独执行程序来执行我想要排队的任务,并且一次只运行一个。 另一种方法是只编写几个任务并提交一个,
executor.submit(new Runnable() {
public void run() {
myTask1.call();
myTask2.call();
myTask3.call();
}});
如果myTask2
引发异常,如果仍希望myTask1
运行,则可能需要更详细。
答案 2 :(得分:1)
我这样做的方法是通过一些自行开发的代码,根据任务所说的密钥将工作流转换到不同的线程(这可以是完全任意的或有意义的值)。而不是提供给Queue
并让其他一些线程取消它(或者在你的情况下向ExecutorService
提供服务并让服务维护一个脱掉内部工作队列的线程池),您向Pipelineable
提供PipelineManager
(也称为任务),该ExecutorService
找到该任务密钥的正确队列,并将任务粘贴到该队列上。有各种其他代码可以管理脱离队列的线程,以确保始终只有1个线程从该队列中取出,以保证为同一个键提供的所有工作都将按顺序执行。
使用这种方法,您可以轻松地为n组串行工作留出某些键,同时对可以按任何旧顺序进行的工作的剩余键进行轮循,或者您可以通过明智的键保持某些管道(线程)热选择。
这种方法对于JDK BlockingQueue
实现是不可行的,因为它们由单个ThreadPoolExecutor
支持(至少是singleThreadExecutor
),因此没有办法说“做”这项工作在任何旧的顺序,但这项工作必须序列化“。我假设你当然希望保持吞吐量,否则根据danben的评论将所有内容都放在ExecutorService
上。
(编辑)
为了保持相同的抽象,你可以做的是创建你自己的ThreadPoolExecutor
实现,它根据你的需要委托给public class PipeliningExecutorService<T extends Pipelineable> implements ExecutorService {
private Map<Key, ExecutorService> executors;
private ExecutorService generalPurposeExecutor;
// ExecutorService methods here, for example
@Override
public <T> Future<T> submit(Callable<T> task) {
Pipelineable pipelineableTask = convertTaskToPipelineable(task);
Key taskKey = pipelineable.getKey();
ExecutorService delegatedService = executors.get(taskKey);
if (delegatedService == null) delegatedService = generalPurposeExecutor;
return delegatedService.submit(task);
}
}
public interface Pipelineable<K,V> {
K getKey();
V getValue();
}
(或类似)的多个实例; 1由n个线程和1个或多个单线程实例支持。像下面这样的东西(根本不是工作代码,但希望你能得到这个想法!)
ExecutorService
为此目的,这是非常丑陋的,{{1}}方法是通用的而不是服务本身,这意味着你需要一些标准的方法来编组传递给Pipelineable的任何内容,如果可以的话,还需要后备。 t(例如将其扔到通用池中)。
答案 3 :(得分:0)
class SequentialExecutorWrapper implements Runnable
{
final private ExecutorService executor;
// queue of tasks to execute in sequence
final private Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<Runnable>();
// semaphore for pop() access to the task list
final private AtomicBoolean taskInProcess = new AtomicBoolean(false);
public void submit(Runnable task)
{
// add task to the queue, try to run it now
taskQueue.offer(task);
if (!tryToRunNow())
{
// this object is running tasks on another thread
// do we need to try again or will the currently-running thread
// handle it? (depends on ordering between taskQueue.offer()
// and the tryToRunNow(), not sure if there is a problem)
}
}
public void run()
{
tryToRunNow();
}
private boolean tryToRunNow()
{
if (taskInProcess.compareAndSet(false, true))
{
// yay! I own the task queue!
try {
Runnable task = taskQueue.poll();
while (task != null)
{
task.run();
task = taskQueue.poll();
}
}
finally
{
taskInProcess.set(false);
}
return true;
}
else
{
return false;
}
}