我通过以下方式创建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是否具有自己的变量,或者它们被共享了?请帮助解释这一点。
当试图了解什么是线程安全的时,我感到非常困惑。因此,这可能是一个非常幼稚的问题。
答案 0 :(得分:2)
每个新的AbcRunnable
实例将具有其自己的字段集(list1
和map1
)。由于您的循环在每次迭代中都调用factory.get()
,并且会创建一个新的AbcRunnable
,因此每个线程池任务将具有runnable及其包含字段的唯一实例。
现在,您尚未显示如何初始化AbcRunnable
中的字段:
List
和Map
实例,则线程之间不会共享任何内容,并且代码是线程安全的。AbcRunnable
实例可能会共享对同一列表/地图的引用,因此您需要确保对数据的同步访问(或使用并发集合实现,它已经是线程安全的。答案 1 :(得分:0)
答案取决于您如何实例化可运行对象。这里有很多事情,所以让我们简化一下。假设我们有很多要求和的n个数字。我们可以将集合分成两部分并创建2个线程,当它们返回时,我们将两个结果相加。因为我们可以将集合一分为二并在最后求和,所以没有共享,因此一切都是线程安全的。
现在让我们说,我们想知道我们的n个数字在工作时已经相加了。我们需要一个共享计数器,每个线程可以随着两个线程的总和而增加。因此,如果计数器为100,并且两个线程都尝试同时增加它,则两个线程都将读取100加1并返回101到内存中,新的计数将为101,但实际上已经将102个数字相加了。对于像我们的计数器这样的共享变量,我们需要确保一次写入一个线程一次只能访问一个线程。
在您的情况下,如果您将相同的列表或映射发送到两个线程,则会出现问题,因为列表和映射是通过引用传递的,或者内存中的地址是发送给新线程的地址,因此两者都可能尝试修改它们同时。但是,如果在发送列表之前将列表拆分并映射为不同的值,则应该没事。