所以,让我说我有4个线程,它们都循环遍历一个包含100个索引的数组,翻转每个索引中的信息位并写回该索引...
arr[];
Thread 1:
for (int i = 0; i< 100; i+=4) { flip bits of arr[i]}
Thread 2:
for (int j = 1; j< 100; j+=4) { flip bits of arr[j]}
Thread 3:
for (int k = 2; k< 100; k+=4) { flip bits of arr[k]}
Thread 4:
for (int l = 3; l< 100; l+=4) { flip bits of arr[l]}
我是并发的总菜鸟,所以我想知道这是不是很好的做法,还是有另外一种做法?
更新:要清楚 - 如果&#34;翻转arr [i]&#34;和&#34;翻转arr [j]&#34;由于某种原因触摸相同的对象/成员,答案&#34;不是线程安全&#34;很明显(并且与数组或实际问题无关),因此假设这些操作不会直接或在某些更深层次的对象中触及不同i和j对的相同内存。
答案 0 :(得分:5)
你没有提供足够的细节,以便人们可以给出一个明确的答案,所以我会做一些额外的假设。
如果你有:
int[] arr = new int[100]; //populate array
t.start()
,然后在这些线程中运行循环,确保每个线程在不同的索引子集上运行(在您的情况下就是这种情况)由于for循环中的步骤4而导致的原始代码t.join()
然后你有以下保证:
int
,因此它们不会相互干扰以下是一些一般性评论。
正如另一个答案所指出的那样,如果性能受到关注,你应该尝试以更加缓存的方式编写代码。
如果所有每个线程都执行25个整数的一些简单位操作,则启动一个线程可能比操作本身花费更多时间,并且顺序运行整个事件会更快。如果您已经运行了一个线程池(例如ExecutorService),那么提交任务的开销可能会小到足以提供增益而不是顺序执行。
如果您乐意支付一些性能开销,您还可以使用java 8的并行流,这样您就可以编写更少的代码:
IntStream.range(0, arr.length).parallel()
.forEach(i -> flip(arr[i]));
在任何情况下,你都应该衡量你所做的事情是否明智......
Thread.start()
/ Thread.join()
在关系之前创建一个事件:JLS #17.4.5
- 在线程中对
start()
的调用发生在启动线程中的任何操作之前。- 线程中的所有操作都发生在任何其他线程从该线程上的
join()
成功返回之前。
每个数组元素都是独立的:JLS #17.6
特别是,分别更新字节数组的相邻元素的两个线程不得干涉或交互,也不需要同步以确保顺序一致性。
更确切地说,这是在JLS #17.4.1中定义的,它表示为了多线程的目的,每个数组元素都被视为一个单独的变量:
在本章中,我们使用术语变量来指代字段和数组元素。
答案 1 :(得分:1)
由于其他线程没有触及数组索引,这应该没问题。尽管如此,你仍然有很多虚假的分享。更好的方法是将索引0-24给线程A,25-49给线程B等等。这样可以更加缓存友好。
答案 2 :(得分:0)
我认为你的代码只是伪代码。 (它不是标记的Java代码,所以也许您最好为我们提供真实的示例代码)。但我会按照你的想法去做。
您有一个数据结构arr
。
你有四个线程同时修改arr
。
您的代码不是线程安全的。
为了使其线程安全,您可以使用互斥锁(处理并发的技术之一)。
围绕arr
包裹互斥锁。这将锁定 arr
,只有拥有互斥锁的线程才能访问arr
。
互斥锁应该在for循环的开头获得,并在for循环结束时释放。
循环将竞争互斥锁,顺序通常是不可预测的。
P.S。:如果您可以断言没有arr
元素引用另一个arr
元素引用的对象,那么您可能不需要过多关注线程安全性。但最好真的编写线程安全代码。