相同的Runnable类被传递两次到同时运行两个/多个线程的ExecutorService

时间:2012-10-31 15:40:31

标签: java multithreading runnable executorservice

我有多个可运行的类,我想通过集中式类运行它们与执行器服务(称之为包含所有可运行列表的Launcher类)。

我写了一个Launcher类,它使用applicationContext.getBean()来实例化所有bean。每个runnable类还为它定义pool size(为此runnable生成的线程数)。

public class DaemonsLauncher {

    @Autowired
    private ApplicationContext appContext;

    private List<Daemon> daemons = new ArrayList<Daemon>();

    private ScheduledThreadPoolExecutor executor;

    private void initThreadPool() throws Exception {
        //daemonNames coming at run time from somewhere.
        List<String> daemonList = Arrays.asList(daemonNames.split(Constant.COMMA));
        //Pool size will now be summation of all the pool sizes.
        int poolSize = 0;
        for (String dName : daemonList) {
            Daemon daemon = appContext.getBean(dName, Daemon.class);
            poolSize += daemon.getPoolSize();
            daemons.add(daemon);
        }
        executor = new ScheduledThreadPoolExecutor(poolSize);
    }

    public void launchDaemons() throws Exception {
        for (Daemon daemon : daemons) {
            for (int currentThreadCount = 1; currentThreadCount <= daemon.getPoolSize(); currentThreadCount++) {
                executor.scheduleWithFixedDelay(daemon, 0, XYZ, ABC);
            }
        }
    }
}

在执行此过程中,我将所有runnable添加到List<Daemon>(其中Daemon是一个抽象的可运行类,由其他守护进程扩展)。

public abstract class Daemon implements Runnable {

    protected int poolSize = 1;

    public int getPoolSize() {
        return poolSize;
    }

    @Override
    public void run() {
        //Calls run methods of individual runnables here.
    }

}

如您所见,我在执行时多次添加每个runnable类的相同实例(取决于poolSize)。

executorScheduledThreadPoolExecutor

此时,调用R的run方法,我发现runnable类的同一个实例,因为它们只被appContext.getBean()一次(通过打印hasCode和toString检查)。

但是这些runnable类的多个实例同时运行。

这种情况可能会出现什么问题?这些为同一个runnable运行的多个线程是否会导致问题,或者它会好起来吗?

作为替代方案,取决于poolSize,我甚至可以在此循环之前获取Runnable类的多个实例,并且只循环一次,具体取决于List中存在的所有“Runnable”条目。

  1. 所有bean本质上都是原型的(虽然它不会出现,因为我只调用了一次appContext.getBean)。
  2. A sample Runnable class

    public class R extends Daemon {
        @Override
        public void runIndividualDaemon() {
            //Called by run of Daemon class.
            logger.info("Running Daemon." + this.hashCode() + " " + this.toString());
        }
    
        @Override
        protected void init() throws Exception {
            this.poolSize = 5;
        }
    }
    

2 个答案:

答案 0 :(得分:2)

如果您正在执行多次任务,那是因为您多次添加它。

我建议你在添加任务时进行记录以确认这一点。

  

但是,同一个线程执行两个线程的任务怎么会发生呢?

线程不能同时执行两个任务,但它可以执行一个又一个任务,重新使用相同的线程。

  

是否“我有'Runnable'类的两个实例”如果那个bean是单例的话,情况就不会这样了?

我不相信添加两个相同的任务或两次添加相同的任务会在这种情况下产生很大的不同。

答案 1 :(得分:0)

由于Daemon类不是线程安全的,因此从多个线程运行相同的实例(类型为Runnable)是危险的。除非使用监视器(synchronized关键字)保护对象的访问权限,否则从多个线程运行同一实例的方法总是很危险的。

然后,池中的所有线程只执行一个Runnable对象。线程池设计用于我们有许多小的Runnable但不想为每个实例花费一个线程的用例。由于您的任务是轮询数据库,因此为每个任务创建一个线程是合乎逻辑的,但是您不需要创建线程池:只需为每个任务启动一个新线程(包括daemonList中某些类的多个线程)。

你写道:“但是这些runnable类的多个实例同时运行”。当然,因为你这样编程。线程可以同时运行。如果线程将Runnable类型的相同或不同对象作为要执行的任务,则无关紧要。