如果要更改,Runnable是否共享数据结构?

时间:2019-05-04 03:04:44

标签: java multithreading runnable

我通过以下方式创建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。我的问题是,不同的Runnable是否具有自己的变量,或者它们被共享了?请帮助解释这一点。

当试图了解什么是线程安全的时,我感到非常困惑。因此,这可能是一个非常幼稚的问题。

2 个答案:

答案 0 :(得分:2)

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

现在,您尚未显示如何初始化AbcRunnable中的字段:

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

答案 1 :(得分:0)

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

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

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