【问题标题】:Does Runnable share the data structure if they are getting changed?如果它们发生变化,Runnable 是否共享数据结构?
【发布时间】:2019-05-04 03:04:43
【问题描述】:

我正在通过以下方式创建一个 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 有自己的变量还是共享?请帮忙解释一下。

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

【问题讨论】:

  • 你为什么要费心包装线程池执行器?制造工厂玻璃?你想解决什么问题?你没有发现Executors 类吗?

标签: java multithreading runnable


【解决方案1】:

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

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

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

【讨论】:

  • 感谢您的回答。我已经编辑了我的问题,以包括如何实例化 runnable 中的变量。现在可以告诉我吗?
  • @hatllla: QweRst 在工厂初始化时只创建一次,所以上面的第二种情况适用。每个 runnable 中的字段都指向相同的共享引用,因此您需要实现额外的同步。
  • @casablance 感谢您的回答。我明白了这个案子。只是基于类似理由的另一个问题是关于方法中的局部变量是否被共享。我在这里问过这个问题:stackoverflow.com/questions/55999721/…。你能在这里回答吗?
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-16
    • 1970-01-01
    • 2016-03-19
    • 2015-05-20
    • 1970-01-01
    • 2019-05-23
    • 1970-01-01
    • 2016-10-03
    相关资源
    最近更新 更多