自定义多线程:限制某些类型的任务并行执行,而不限制其他类型的任务

时间:2015-04-17 11:25:48

标签: java multithreading concurrency threadpool

我有3种类型的任务:A, B, C

我想在N个线程中并行运行这些任务。让我们假设,任务列表如下:

A, B, C, B, C, A, B, C

当然,我可以使用ExecutorService实现多线程执行但问题是我需要一次最多执行C类型的任务。其他类型C的任务必须按顺序执行,但与任务A和/或B并行执行。

例如,3线程执行程序可能处于以下任何状态:

A B C
A A A
A C B
B B C
B B B
B C
A C 
A B 
C  
... 

(允许一次执行A或B类型的多个任务,但一次只能执行最多一个C类任务

有没有办法在Java中实现这个目标?

更新

这是我遇到的问题这是一个正确的方法吗? 在这里,我通过ExecutorService执行所有任务。执行时我将检查是否有任何其他C任务正在运行。如果不是我将执行其他我将把它添加到一个队列将在成功完成任何其他任务后出列,并且我检查任何C任务是否正在运行

public class Test {

public void startExecution() {
    Queue<String> runQ = new LinkedList<>();
    ThreadPool exec = (ThreadPool) Executors.newFixedThreadPool(RunSettings.getRunSettings().getThreadCount());
    while (!runQ.isEmpty() && !SystemDefaults.stopExecution.get()) {
        String TaskName = runQ.remove();
        Task t = new Task(TaskName);
        exec.execute(t, TaskName);

    }
    exec.shutdown();
    if (exec.awaitTermination(RunSettings.getRunSettings().getExecutionTimeOut(), TimeUnit.MINUTES)) {
        System.out.println("[CONTROL: ALL TEST TASKS COMPLETED SUCCESSFULLY.]");
    } else {
        System.out.println("[CONTROL: ALL THE TEST TASKS DID NOT COMPLETE SUCCESSFULLY IN STIPULATED TIME. FORCEFULLY FINALIZING.]");
        exec.shutdownNow();

    }
}
}

ThreadPool我创建的内容

public class ThreadPool extends ThreadPoolExecutor {

public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}

final String CTask = "TaskC";
Map<Runnable, String> TaskPool = new HashMap<>();
Queue<Runnable> TaskCList = new LinkedList<>();

@Override
protected void afterExecute(Runnable r, Throwable t) {
    super.afterExecute(r, t);
    if (TaskPool.containsKey(r)) {
        TaskPool.remove(r);
    }
    if (!TaskPool.containsValue(CTask) && !TaskCList.isEmpty()) {
        Runnable ieRun = TaskCList.remove();
        super.execute(ieRun);
        TaskPool.put(ieRun, CTask);
    }
}

public void execute(Runnable command, String TaskType) {
    if (TaskPool.containsValue(TaskType)
            && TaskType.equalsIgnoreCase(CTask)) {
        System.out.println("Another Instance of  " + CTask + " Running");
        TaskCList.add(command);
    } else {
        super.execute(command);
        TaskPool.put(command, TaskType);
    }
}

}

3 个答案:

答案 0 :(得分:2)

最简单的方法是创建2个执行程序:一个用于C类任务的单线程,另一个用于其他类型任务的多线程:

class ExecutorWrapper {
    private ExecutorService forC = Executors.newSingleThreadExecutor();
    private ExecutorService forAnother = Executors.newFixedThreadPool(THREAD_NUMBER);

    public void acceptTask(Runnable r) {
        if (r instanceof TaskC) {
            forC.execute(r);
        } else {
            forAnother.execute(r);
        }
    }
}

现在,类型C的任何任务都将在forC执行程序内部队列中等待,直到此类型的另一个任务完成。

如果您不想创建其他执行程序,则需要实现某种并发控制,由于可能出现的竞争条件,这种控制更加复杂且难以调试。我可以提出解决方案草案,但没有代码:

  1. 创建一个标志以指示是否已在执行另一个任务C,以及另一个任务C将等待的队列
  2. 当C类任务到达时,检查另一个任务C是否正在执行,如果是,则将其添加到提到的队列中
  3. 在任务C完成时,发送任务C已完成的某种通知 - 以便从提到的队列中获取下一个任务C并将其发送到Executor。如果queue为空,请清除该标志以指示现在没有任务C正在执行。可以通过使用Callable包装任务C并调用将阻塞直到任务完成的Future#get方法来实现此类通知。

答案 1 :(得分:1)

感谢大家的支持。无论如何,我想出了一个目前运作良好的解决方案

public class Test {

public void startExecution() {
Queue<String> runQ = new LinkedList<>();
ThreadPool threadPool = new ThreadPool(threadCount,timeOut);
while (!runQ.isEmpty()) {
    String TaskName = runQ.remove();
    Task t = new Task(TaskName);
    threadPool.execute(t, TaskName);

}
if (threadPool.awaitTermination(timeOut, TimeUnit.MINUTES)) {
    System.out.println("[CONTROL: ALL TEST TASKS COMPLETED SUCCESSFULLY.]");
} else {
    System.out.println("[CONTROL: ALL THE TEST TASKS DID NOT COMPLETE SUCCESSFULLY IN STIPULATED TIME. FORCEFULLY FINALIZING.]");
    threadPool.shutdownNow();

}
}
}

ThreadPool实现

 public class ThreadPool extends ThreadPoolExecutor {

public ThreadPool(int threadCount, long keepAliveTime) {
    super(threadCount, threadCount, keepAliveTime, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>());
}

final String CTask = "TaskC";
Map<Runnable, String> TaskPool = new HashMap<>();
Queue<Runnable> TaskCList = new LinkedList<>();

@Override
protected synchronized void afterExecute(Runnable r, Throwable t) {
    super.afterExecute(r, t);
    System.out.println(TaskPool.get(r) + "Finished");
    if (TaskPool.containsKey(r)) {
        TaskPool.remove(r);
    }
    if (TaskCList.isEmpty()) {
        super.shutdown();
    }
    if (!TaskPool.containsValue(CTask) && !TaskCList.isEmpty()) {
        if (super.getActiveCount() < super.getCorePoolSize()) {
            System.out.println("Trying to execute Other C Tasks");
            Runnable ieRun = TaskCList.remove();
            super.execute(ieRun);
            TaskPool.put(ieRun, CTask);
        }
    }
}

public synchronized void execute(Runnable command, String TaskType) {
    if (TaskPool.containsValue(TaskType)
            && TaskType.equalsIgnoreCase(CTask)) {
        System.out.println("Another Instance of TaskC Running");
        System.out.println("Added for future Execution");
        TaskCList.add(command);
    } else {
        System.out.println("Adding " + TaskType + " to execution");
        TaskPool.put(command, TaskType);
        super.execute(command);
    }
}

答案 2 :(得分:-1)

看起来您需要做的就是创建任务C synchronized。这里有一些测试代码似乎证明这已经足够了 - 尽管没有失败并不总是意味着成功。

结果清楚地表明A&B和B并行运行,但从未发生过C

static enum Task implements Callable<Void> {

    A,
    B,
    C {
                @Override
                public synchronized Void call() throws Exception {
                    if (running.get(this).get() != 0) {
                        System.out.println("FAIL!");
                    }
                    return super.call();
                }

            };

    // How many of each are running.
    static Map<Task, AtomicInteger> running = Stream.of(Task.values())
            .collect(
                    Collectors.toMap(
                            (t) -> t,
                            (t) -> new AtomicInteger(0),
                            (x, y) -> x,
                            () -> new EnumMap<Task, AtomicInteger>(Task.class)));

    // List all running tasks.
    private String runningList() {
        StringBuilder s = new StringBuilder();
        running.entrySet().stream().forEach((r) -> {
            if (r.getValue().get() != 0) {
                s.append(r.getKey()).append("=").append(r.getValue()).append(",");
            }
        });
        return s.toString();
    }

    static final Random random = new Random();

    @Override
    public Void call() throws Exception {
        System.out.println("Running " + name() + " with " + runningList());
        // Mark me running.
        running.get(this).getAndIncrement();
        // Hang around for a bit.
        Thread.sleep(random.nextInt(1000));
        // Mark me not running.
        running.get(this).getAndDecrement();
        return null;
    }

}

// The pool.
static ExecutorService pool = Executors.newFixedThreadPool(5);
// The tasks.
static Task[] tasks = new Task[]{Task.A, Task.B, Task.C, Task.B, Task.C, Task.A, Task.B, Task.C,};

public void test() throws InterruptedException {
    // Run 10 times.
    for (int i = 0; i < 10; i++) {
        pool.invokeAll(Arrays.asList(tasks));
    }
    pool.shutdown();
    pool.awaitTermination(10, TimeUnit.SECONDS);
}
相关问题