如果 Runnable 发生变化,它们是否共享数据结构?

Does Runnable share the data structure if they are getting changed?

我正在通过以下方式创建一个 Runnable:

public class AbcRunnable implements Runnable
{
     Qwe qwe;

     Rst rst;

     public void run() {

        // some operations on qwe and rst which are changing their value
     }
}

public class AbcThreadPool {

    private final AbcThreadPoolExecutor executor;

    public InventoryAvailabilityThreadPool(final AbcRunnableFactory factory,
                                           final Integer poolSize) {
        executor = new AbcThreadPoolExecutor(factory, poolSize);

        for (int i = 0; i < poolSize; ++i) {
            executor.execute(factory.get());
        }
    }

    private static class AbcThreadPoolExecutor extends ThreadPoolExecutor {

        private final AbcRunnableFactory factory;

        public AbcThreadPoolExecutor(final AbcRunnableFactory factory,
                                                       final int poolSize) {
            super(poolSize, poolSize, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
            this.factory = factory;
            allowCoreThreadTimeOut(false);
        }
    }
}

public class AbcRunnableFactory {

    @Override
    public AbcRunnable get() {
        return new AbcRunnable();
    }
}

Qwe 和 Rst 的初始化由 guice 模块完成,如下所示:

@Provides
@Singleton
private AbcRunnableFactory provideAbcRunnableFactory() {
    return new AbcRunnableFactory(
        new Qwe(), new Rst());
}

所以,这里的 AbcRunnable 有 2 个变量:qwe 和 rst。我的问题是,不同的 Runnables 有自己的变量还是共享?请帮忙解释一下。

当我试图理解什么是线程安全的与否时,我很困惑。所以,这可能是一个非常幼稚的问题。

AbcRunnable 的每个新实例都有自己的一组字段(list1map1)。由于您的循环在每次迭代中调用 factory.get(),并且会创建一个新的 AbcRunnable,因此每个线程池任务将具有一个唯一的可运行实例及其包含的字段。

现在,您还没有展示如何初始化 AbcRunnable 中的字段:

  • 如果您在构造函数中创建新的 ListMap 实例,则线程之间不会共享任何内容,您的代码是线程安全的。
  • 如果您从外部传入这些值中的任何一个,那么您的不同 AbcRunnable 实例可能会共享对同一 list/map 的引用,您将需要确保同步访问数据(或者使用已经是线程安全的并发集合实现)。

答案取决于您如何实例化可运行文件。这里有很多事情要做,所以让我们简化一下。假设我们有一组非常大的 n 个数字要求和。我们可以将集合一分为二并创建 2 个线程,当它们 return 时,我们只需将两个结果相加。因为我们可以将集合一分为二,最后求和,所以没有任何共享,所以一切都是线程安全的。

现在假设我们想知道 n 个数字中有多少在计算时被求和了。我们需要一个共享计数器,每个线程都可以随着两个线程的总和而递增。因此,如果计数器为 100 并且两个线程都尝试同时递增它,则两个线程都将读取 100 加 1 和 return 101 到内存中,新计数将为 101,但实际上 102 个数字已经相加。对于像我们的计数器这样的共享变量,如果他们正在写入,我们需要确保一次只有一个线程可以访问它。

在你的情况下,如果你将相同的列表或映射发送到两个线程,你会遇到问题,因为列表和映射是通过引用传递的,或者内存中的地址是发送到新线程的地址,所以两者都可能会尝试同时修改它们。但是,如果您在发送之前拆分列表并映射到不同的值,那么您应该没问题。