在多个线程中洗牌阵列

时间:2011-01-19 13:48:11

标签: java multithreading concurrency jvm

我有一个大小为N的数组。我想在2个线程(或更多)中将其元素洗牌。每个线程都应该使用它自己的数组部分。

假设第一个线程将元素从0移动到K,第二个线程将元素从K移动到N(其中0

//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);
}

JVM是否有任何保证,我会在这样的改组之后看到array方法中main的变化?

我应该如何实施ShufflingThread(或者,我应该如何运行它,可能在synchronized区块内或其他任何区域内)以获得此类保证?

5 个答案:

答案 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)

好吧,他们不能同时访问同一个数组,如果你使用锁,互斥或任何其他同步机制,你就失去了线程的力量(因为你必须等待另一个,要么完成洗牌或完成一些改组)。 你为什么不把数组分成两半,给每个线程一个数组,然后合并两个数组?