Java中的并发 - 将任务委派给工作线程,我做得对吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中的并发 - 将任务委派给工作线程,我做得对吗?相关的知识,希望对你有一定的参考价值。

所以我正在为一个在线游戏制作一个模拟器,我似乎无法想出一个很好的方法来处理同时运行的大量任务。显然,在一个线程上加载所有内容都不起作用。

我的想法是有一个主线程将任务委托给x个工作线程。一旦主线程完成排队任务,它就会发信号通知工人开始解雇任务并停止,直到他们完成。我的实现如下:

package com.rs2.engine;

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CountDownLatch;
import java.util.ArrayList;

import com.rs2.util.Timer;

public class Engine implements Runnable {

    private ScheduledExecutorService scheduledExecutorService;
    private ExecutorService executorService;
    private int currentTick;
    private ArrayList<Task> tasks;
    private Timer timer;

    public Engine(int workers) {
        this.executorService = Executors.newFixedThreadPool(workers);
        this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        this.currentTick = 0;
        this.tasks = new ArrayList<>(10000);
        this.timer = new Timer();
    }

    public int getCurrentTick() {
        return currentTick;
    }

    public ExecutorService getWorkers() {
        return executorService;
    }

    public void start() {
        this.scheduledExecutorService.scheduleAtFixedRate(this, 0, 600, TimeUnit.MILLISECONDS);
    }

    public void cycle() {

    }

    public void queueTask(Task task) {
        tasks.add(task);
    }

    public void processQueuedTasks() {
        try {
            CountDownLatch latch = new CountDownLatch(tasks.size());
            for (int i = 0; i < tasks.size(); i++) {
                Task t = tasks.get(i);
                t.setCountDownLatch(latch);
                executorService.submit(t);
            }
            latch.await();
            tasks.clear();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        timer.reset();
        cycle();
        currentTick++;
        //System.out.println("Cycle time: "+timer.elapsed());
    }

}

queueTask()和processQueuedTasks()仅从主线程调用。此引擎用于处理服务器需要执行的任何和所有任务。首先,它处理网络事件(传入的数据包),然后更新实体和其他事件。这就是为什么我喜欢把它抽象化。

这是Task类以及:

package com.rs2.engine;

import java.util.concurrent.CountDownLatch;

public class Task implements Runnable {

    private CountDownLatch latch;

    public Task() {
    }

    @Override
    public void run() {
        execute();
        latch.countDown();
    }

    public void execute() {

    }

    public void setCountDownLatch(CountDownLatch latch) {
        this.latch = latch;
    }

}

我的问题如下:

  1. 在Engine类中,在并发性方面使用常规ArrayList是否可以?
  2. 有没有更好的方法将任务排队到ExecutorService?如果有太多任务同时排队,我觉得它可能会导致问题。
  3. 在我开始重新发明轮子之前,是否有任何我应该关注的引擎框架?
答案

如果您担心可能在ExecutorService中排队太多任务,可以使用Semaphore来限制它一次可以运行的任务。

将它放在你的processQueuedTasks()方法for循环中,以限制要运行的任务数量。

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html

另一答案

您可以设置线程池中的线程数,而不是使用Semaphore来限制并发运行的任务数。您将只获得与线程同时运行的任务数

Executors.newFixedThreadPool(n);

public void queueTask(Task task) {由于此方法是公共的,因此可以从任何线程调用它,因此ArrayList不会是线程安全的。想想如果您在将任务提交给执行程序期间尝试对任务进行排队,会发生什么。您已从线程池执行程序“借用”了功能,并通过拥有任务集合将其放入代码中。

以上是关于Java中的并发 - 将任务委派给工作线程,我做得对吗?的主要内容,如果未能解决你的问题,请参考以下文章

《Java并发编程实战》----线程池的使用

Java编程思想-并发

java并发基础

节点集群:仅向一名工作人员处理任务

java并发编程线程池的使用

Java 多线程进阶-并发编程 线程组ThreadGroup