Partitioning and analyzing a java array with multi

2020-06-24 05:03发布

问题:

I have to initialize a float[12000] via a for loop 12000 times. I then scan the array to look for values exceeding a certain threshold value. If the value exceeds the threshold, I manipulate an instance variable of a certain object.

Example:

Random random = new Random();
float[] x = new float[12000];

for (int i = 0; i < x.length; i++) {
  x[i] = random.nextFloat();
}

for (int i = 0; i < x.length; i++) {
  if (x[i] >= 0.75) {
  \\ do something interesting
  }
}

Basically, I have to change the values of the array and do this 12000 times on a new array each time of length 12000. The "something interesting" code is merely looking up that index in another data structure and calling a setter. From my System time calculations, it should take me about 13 hours. I have 8 processors on my machine.

How can I take advantage of java's multi-threading capabilities? I am specifically looking for thread solutions that partition up the initializing and scanning of the arrays. Source code using threads would be appreciated.

回答1:

You can divide this up among eight different threads doing something like this

public class Worker implements Runnable {
    final private int minIndex; // first index, inclusive
    final private int maxIndex; // last index, exclusive
    final private float[] data;

    public Worker(int minIndex, int maxIndex, float[] data) {
        this.minIndex = minIndex;
        this.maxIndex = maxIndex;
        this.data = data;
    }

    public void run() {
        for(int i = minIndex; i < maxIndex; i++) {
            if(data[i] >= 0.75) {
                // do something interesting
            }
        }
    }
}


// *** Main Thread ***
float[] data = new float[12000];
int increment = data.length / 8;
for(int i = 0; i < 8; i++) {
    new Thread(new Worker(i * increment, (i + 1) * increment, data)).start();
}

This divides up the array among the 8 different threads. Or, another option is this:

public class Worker implements Runnable {
    final private BlockingQueue<Integer> queue;
    final private float[] data;

    public Worker(BlockingQueue<Integer> queue) {
        this.queue = queue;
        this.data = data;
    }

    public void run() {
        while(true) {
            int i = queue.take();
            float f = data[i];
            // do something interesting to f
        }
    }
}


// *** Main Thread ***
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
float[] data = new float[12000];
for(int i = 0; i < 8; i++) {
    new Thread(new Worker(queue, data)).start();
}
for(int i = 0; i < data.length; i++) {
    if (data[i] >= 0.75) {
        queue.offer(i);
    }
}

This uses one thread to iterate through the array and find the interesting numbers, and then uses eight worker threads to do something interesting to the interesting numbers. I'd tend to prefer this approach, as it's possible with the first approach that one worker thread would wind up having to process a thousand interesting numbers while another worker thread only needs to process a few interesting numbers; this approach ensures that each thread needs to process approximately the same quantity of interesting numbers.

I'm omitting a lot of stuff, like how to use Executors and how to shut down your worker threads etc - here's a tutorial on that.

Edit To take your code and run it 12000 times on 8 threads, you would do the following:

public class Worker implements Runnable {
    private final int numberOfIterations;
    private final float[] x = new float[12000];

    public Worker(int numberOfIterations) {
        this.numberOfIterations = numberOfIterations;
    }

    public void run() {
        for(int i = 0; i < numberOfIterations; i++) {
            Random random = new Random();

            for (int i = 0; i < x.length; i++) {
                x[i] = random.nextFloat();
            }

            for (int i = 0; i < x.length; i++) {
                if (x[i] >= 0.75) {
                    \\ do something interesting
                }
            }
        }
    }
}


// *** Main Thread ***
Thread[] threads = new Thread[8];
for(int i = 0; i < 8; i++) {
    threads[i] = new Thread(new Worker(12000/8));
    threads[i].start();
}
for(int i = 0; i < 8; i++) {
    threads[i].join();
}

Each of the eight threads will run 1500 iterations of the "initialize float array, iterate through float array" code. The join method will then wait for the threads to finish. Be certain that the code in // do something interesting is thread-safe - you said that you're calling a setter, so be certain that multiple threads won't be calling the same setter, or else that the setter is synchronized, or else that you're using something like an AtomicInteger in the setter. Post the setter code if you have any doubts about it.