如果 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
的每个新实例都有自己的一组字段(list1
和 map1
)。由于您的循环在每次迭代中调用 factory.get()
,并且会创建一个新的 AbcRunnable
,因此每个线程池任务将具有一个唯一的可运行实例及其包含的字段。
现在,您还没有展示如何初始化 AbcRunnable
中的字段:
- 如果您在构造函数中创建新的
List
和 Map
实例,则线程之间不会共享任何内容,您的代码是线程安全的。
- 如果您从外部传入这些值中的任何一个,那么您的不同
AbcRunnable
实例可能会共享对同一 list/map 的引用,您将需要确保同步访问数据(或者使用已经是线程安全的并发集合实现)。
答案取决于您如何实例化可运行文件。这里有很多事情要做,所以让我们简化一下。假设我们有一组非常大的 n 个数字要求和。我们可以将集合一分为二并创建 2 个线程,当它们 return 时,我们只需将两个结果相加。因为我们可以将集合一分为二,最后求和,所以没有任何共享,所以一切都是线程安全的。
现在假设我们想知道 n 个数字中有多少在计算时被求和了。我们需要一个共享计数器,每个线程都可以随着两个线程的总和而递增。因此,如果计数器为 100 并且两个线程都尝试同时递增它,则两个线程都将读取 100 加 1 和 return 101 到内存中,新计数将为 101,但实际上 102 个数字已经相加。对于像我们的计数器这样的共享变量,如果他们正在写入,我们需要确保一次只有一个线程可以访问它。
在你的情况下,如果你将相同的列表或映射发送到两个线程,你会遇到问题,因为列表和映射是通过引用传递的,或者内存中的地址是发送到新线程的地址,所以两者都可能会尝试同时修改它们。但是,如果您在发送之前拆分列表并映射到不同的值,那么您应该没问题。
我正在通过以下方式创建一个 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
的每个新实例都有自己的一组字段(list1
和 map1
)。由于您的循环在每次迭代中调用 factory.get()
,并且会创建一个新的 AbcRunnable
,因此每个线程池任务将具有一个唯一的可运行实例及其包含的字段。
现在,您还没有展示如何初始化 AbcRunnable
中的字段:
- 如果您在构造函数中创建新的
List
和Map
实例,则线程之间不会共享任何内容,您的代码是线程安全的。 - 如果您从外部传入这些值中的任何一个,那么您的不同
AbcRunnable
实例可能会共享对同一 list/map 的引用,您将需要确保同步访问数据(或者使用已经是线程安全的并发集合实现)。
答案取决于您如何实例化可运行文件。这里有很多事情要做,所以让我们简化一下。假设我们有一组非常大的 n 个数字要求和。我们可以将集合一分为二并创建 2 个线程,当它们 return 时,我们只需将两个结果相加。因为我们可以将集合一分为二,最后求和,所以没有任何共享,所以一切都是线程安全的。
现在假设我们想知道 n 个数字中有多少在计算时被求和了。我们需要一个共享计数器,每个线程都可以随着两个线程的总和而递增。因此,如果计数器为 100 并且两个线程都尝试同时递增它,则两个线程都将读取 100 加 1 和 return 101 到内存中,新计数将为 101,但实际上 102 个数字已经相加。对于像我们的计数器这样的共享变量,如果他们正在写入,我们需要确保一次只有一个线程可以访问它。
在你的情况下,如果你将相同的列表或映射发送到两个线程,你会遇到问题,因为列表和映射是通过引用传递的,或者内存中的地址是发送到新线程的地址,所以两者都可能会尝试同时修改它们。但是,如果您在发送之前拆分列表并映射到不同的值,那么您应该没问题。