我有一个大小为N的数组。我想在2个线程(或更多)中将其元素洗牌。每个线程都应该使用它自己的数组部分。
假设第一个线程将元素从0移动到K,第二个线程将元素从K移动到N(其中0 JVM是否有任何保证,我会在这样的改组之后看到 我应该如何实施//try-catch stuff is ommited
static void shuffle(int[] array) {
Thread t1 = new ShufflingThread(array, 0, array.length / 2);
Thread t2 = new ShufflingThread(array, array.length / 2, array.length);
t1.start();
t2.start();
t1.join();
t2.join();
}
public static void main(String[] args) {
int array = generateBigSortedArray();
shuffle(array);
}
array
方法中main
的变化? ShufflingThread
(或者,我应该如何运行它,可能在synchronized
区块内或其他任何区域内)以获得此类保证?
答案 0 :(得分:5)
join()
调用足以确保内存一致性:当t1.join()
返回时,主线程“看到”线程t1
对数组执行的操作。
此外,Java保证阵列上没有单词撕裂:不同的线程可以使用同一阵列的不同元素而无需同步。
答案 1 :(得分:2)
我认为这是一个很好的线程控制练习,其中(1)一个工作可以分成几个部分(2)部分可以独立和异步地运行。(3)主线程监视所有这些部分的完成各自职位的工作。每次线程完成执行时,您所需要的只是这个主线程的wait()和notify() - ed jobCount次。这是一个可以编译/运行的示例代码。取消注释println()以查看更多内容。
注意:[1] JVM不保证线程的执行顺序[2]当主线程访问大数组时,需要同步,以免数据损坏....
public class ShufflingArray {
private int nPart = 4, // Count of jobs distributed, resource dependent
activeThreadCount, // Currently active, monitored with notify
iRay[]; // Array the threads will work on
public ShufflingArray (int[] a) {
iRay = a;
printArray (a);
}
private void printArray (int[] ia) {
for (int i = 0 ; i < ia.length ; i++)
System.out.print (" " + ((ia[i] < 10) ? " " : "") + ia[i]);
System.out.println();
}
public void shuffle () {
int startNext = 0, pLen = iRay.length / nPart; // make a bunch of parts
for (int i = 0 ; i < nPart ; i++, activeThreadCount++) {
int start = (i == 0) ? 0 : startNext,
stop = start + pLen;
startNext = stop;
if (i == (nPart-1))
stop = iRay.length;
new Thread (new ShuffleOnePart (start, stop, (i+1))).start();
}
waitOnShufflers (0); // returns when activeThreadCount == 0
printArray (iRay);
}
synchronized private void waitOnShufflers (int bump) {
if (bump == 0) {
while (activeThreadCount > 0) {
// System.out.println ("Waiting on " + activeThreadCount + " threads");
try {
wait();
} catch (InterruptedException intex) {
}}} else {
activeThreadCount += bump;
notify();
}}
public class ShuffleOnePart implements Runnable {
private int startIndex, stopIndex; // Operate on global array iRay
public ShuffleOnePart (int i, int j, int k) {
startIndex = i;
stopIndex = j;
// System.out.println ("Shuffler part #" + k);
}
// Suppose shuffling means interchanging the first and last pairs
public void run () {
int tmp = iRay[startIndex+1];
iRay[startIndex+1] = iRay[startIndex]; iRay[startIndex] = tmp;
tmp = iRay[stopIndex-1];
iRay[stopIndex-1] = iRay[stopIndex-2]; iRay[stopIndex-2] = tmp;
try { // Lets imagine it needs to do something else too
Thread.sleep (157);
} catch (InterruptedException iex) { }
waitOnShufflers (-1);
}}
public static void main (String[] args) {
int n = 25, ia[] = new int[n];
for (int i = 0 ; i < n ; i++)
ia[i] = i+1;
new ShufflingArray(ia).shuffle();
}}
答案 2 :(得分:1)
Thread.start()和Thread.join()足以为你提供数组初始化之间的发生之前的关系,它与线程的交接,然后是在线程中的回读主要方法。
导致事先发生的行为是documented here。
正如其他地方所提到的,ForkJoin非常适合这种分而治之的算法,可以让你摆脱许多你需要实施的簿记。
答案 3 :(得分:0)
使用java.util.Concurrent包中的ExecutorService以及Callable Task从每个线程运行中返回数组的一部分,一旦完成两个线程,就可以采取另一种方式来实现一致的行为。
答案 4 :(得分:-2)
好吧,他们不能同时访问同一个数组,如果你使用锁,互斥或任何其他同步机制,你就失去了线程的力量(因为你必须等待另一个,要么完成洗牌或完成一些改组)。 你为什么不把数组分成两半,给每个线程一个数组,然后合并两个数组?