Multithreading in java - 4 threads that do the same automatically - java

I've written a program to scan for amicable numbers (a pair of 2 numbers that the sum of all devisors of one equals to the other) It works ok and I'll include the entire code below.
I tried to get it to run with several threads so I moved the code to a class called Breaker and my main looks as follows:
Breaker line1 = new Breaker("thread1");
Breaker line2 = new Breaker("thread2");
Breaker line3 = new Breaker("thread3");
Breaker line4 = new Breaker("thread4");
line1.scanRange(1L, 650000L);
line2.scanRange(650001L, 850000L);
line3.scanRange(850001L, 1000000L);
line4.scanRange(1000001L, 1200001L);
Now this does shorten the time noticably, but this is not a smart solution and the threads end each on very different times.
What I'm trying to do, is to automate the process so that a master thread that has the entire range, will fire up sections of short ranges (10000) from the master range, and when a thread ends, to fire up the next section in a new thread, until the entire master range is done.
I've tried understanding how to use synchronized, notify() and wait() but after several tries all ended with different errors and unwanted behaviour.
Here is Breaker.java:
import java.util.ArrayList;
public class Breaker implements Runnable{
Long from, to = null;
String name = null;
Thread t = new Thread(this);
public Breaker(String name){
this.name = name;
}
public void scanRange(Long from, Long to){
this.from = from;
this.to = to;
t.start();
}
#Override
public void run() {
this.scan();
}
private void scan() {
ArrayList<ArrayList<Long>> results = new ArrayList<ArrayList<Long>>();
Long startingTime = new Long(System.currentTimeMillis() / 1000L);
Long lastReport = new Long(startingTime);
System.out.println(startingTime + ": Starting number is: " + this.from);
for (Long i = this.from; i <= this.to; i++) {
if (((System.currentTimeMillis() / 1000L) - startingTime ) % 60 == 0 && (System.currentTimeMillis() / 1000L) != lastReport) {
System.out.println((System.currentTimeMillis() / 1000L) + ": " + this.name + " DOING NOW " + i.toString() + ".");
lastReport = (System.currentTimeMillis() / 1000L);
}
ArrayList<Long> a = new ArrayList<Long>();
a = getFriendPair(i);
if(a != null) {
results.add(a);
System.out.println(this.name + ": FOUND PAIR! " + a.toString());
}
}
System.out.println((System.currentTimeMillis() / 1000L) + ": " + this.name + " Done. Total pairs found: " + results.size() +
". Total working time: " + ((System.currentTimeMillis() / 1000L) - startingTime) + " seconds.");
}
/**
* Receives integer and returns an array of the integer and the number who is it's
* pair in case it has any. Else returns null.
* #param i
* #return
*/
private static ArrayList<Long> getFriendPair(Long i) {
Long possibleFriend = getAndSumAllDevisors(i);
if (possibleFriend.compareTo(i) <= 0) return null;
Long sumOfPossibleFriend = getAndSumAllDevisors(possibleFriend);
if(sumOfPossibleFriend.equals(i)) {
ArrayList<Long> pair = new ArrayList<Long>();
pair.add(i);
pair.add(possibleFriend);
return pair;
}
return null;
}
private static Long getAndSumAllDevisors(Long victim) {
Long sum = new Long(1);
Long i = 2L;
Long k = new Long(0);
while ((k = i * i) <= victim) {
if ((victim % i) == 0) {
sum += i;
if (k == victim) return sum;
sum += (victim / i);
}
i++;
}
return sum;
}
}

Consider ExecutorService, which is backed by a thread pool. You feed it tasks and they get shuffled off to worker threads as they become available:
http://www.vogella.com/articles/JavaConcurrency/article.html#threadpools

What you need is a "Fixed Thread Pool". See http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool%28int%29

I would go for a ExecutorService with a fixed thread pool size. Your master thread can either feed directly to the executor service or you can disconnect them via a BlockingQueue. The Java Doc of the blocking queue describes the producer-consumer pattern quite nicely.

I ended up taking none of the answers but rather Marko's comment and implemented my solution using a Fork/Join framework. It works and runs almost at twice the speed of the none optimized version.
My code looks now like so:
main file (runner)
public class runner {
private static Long START_NUM = 1L;
private static Long END_NUM = 10000000L;
public static void main(String[] args) {
Long preciseStartingTime = new Long(System.currentTimeMillis());
ForkJoinPool pool = new ForkJoinPool();
WorkManager worker = new WorkManager(START_NUM, END_NUM);
pool.invoke(worker);
System.out.println("precise time: " + (System.currentTimeMillis() - preciseStartingTime));
}
WorkManager
I've defined here 3 class variables. from and to are set from a constructor which is called from the main file. And threshold which is the maximum amount of numbers the program will assign a single thread to compute serially.
As you can see in the code, it will recursively make the range smaller until it is small enough to to compute directly, then it calls Breaker to start breaking.
import java.util.concurrent.RecursiveAction;
public class WorkManager extends RecursiveAction{
Long from, to;
Long threshold = 10000L;
public WorkManager(Long from, Long to) {
this.from = from;
this.to = to;
}
protected void computeDirectly(){
Breaker b = new Breaker(from, to);
b.scan();
}
#Override
protected void compute() {
if ((to - from) <= threshold){
System.out.println("New thread from " + from + " to " + to);
computeDirectly();
}
else{
Long split = (to - from) /2;
invokeAll(new WorkManager(from, from + split),
new WorkManager(from + split + 1L, to));
}
}
}
Breaker (is no longer an implementation of of Runnable)
public class Breaker{
Long from, to = null;
public Breaker(Long lFrom, Long lTo) {
this.from = lFrom;
this.to = lTo;
}
public void scan() {
ArrayList<ArrayList<Long>> results = new ArrayList<ArrayList<Long>>();
Long startingTime = new Long(System.currentTimeMillis() / 1000L);
for (Long i = this.from; i <= this.to; i++) {
ArrayList<Long> a = new ArrayList<Long>();
a = getFriendPair(i);
if(a != null) {
results.add(a);
System.out.println((System.currentTimeMillis() / 1000L) + ": FOUND PAIR! " + a.toString());
}
}
}
/**
* Receives integer and returns an array of the integer and the number who is it's
* pair in case it has any. Else returns null.
* #param i
* #return
*/
private static ArrayList<Long> getFriendPair(Long i) {
Long possibleFriend = getAndSumAllDevisors(i);
if (possibleFriend.compareTo(i) <= 0) return null;
Long sumOfPossibleFriend = getAndSumAllDevisors(possibleFriend);
if(sumOfPossibleFriend.equals(i)) {
ArrayList<Long> pair = new ArrayList<Long>();
pair.add(i);
pair.add(possibleFriend);
return pair;
}
return null;
}
private static Long getAndSumAllDevisors(Long victim) {
Long sum = new Long(1);
Long i = 2L;
Long k = new Long(0);
while ((k = i * i) <= victim) {
if ((victim % i) == 0) {
sum += i;
if (k == victim) return sum;
sum += (victim / i);
}
i++;
}
return sum;
}
}

Related

Simulating a Waiting line

I've been tasked as an assignment to make a queue, that is supposed to be simulated 10 times, and for it to have a waiting room that can hold 100 customers.
I was able to simulate it 10 times, but the assignment mentions criteria such as having an average waiting time of x minutes, having a minimum number of served customers, and at the end have a maximum number of those waiting in line.
Here are my classes
Customer.class
public class Customer {
int arrivalTime;
int transactionTime;
int customerNumber;
public Customer(int a, int t, int c) {
arrivalTime = a;
transactionTime = t;
customerNumber = c;
}
public int getArrivalTime() {
return arrivalTime;
}
public int getTransactionTime() {
return transactionTime;
}
public int getCustomerNumber() {
return customerNumber;
}
}
WaitLine.class
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
public class WaitLine {
private static QueueInterface<Customer> line;
private static int numberOfArrivals;
private static int numberServed;
private static int totalTimeWaited;
public WaitLine() {
line = new LinkedQueue<Customer>();
reset();
}
public final void reset() {
line.clear();
numberOfArrivals = 0;
numberServed = 0;
totalTimeWaited = 0;
}
public void simulate(int duration, double arrivalProbability, int maxTransactionTime) {
int transactionTimeLeft = 0;
for (int clock = 0; clock < duration; clock++) {
if (Math.random() < arrivalProbability) {
numberOfArrivals++;
int transactionTime = (int) (Math.random() * maxTransactionTime + 1);
Customer nextArrival = new Customer(clock, transactionTime, numberOfArrivals);
line.enqueue(nextArrival);
System.out.println("Customer " + numberOfArrivals + " enters line at time " + clock
+ ". Transaction time is " + transactionTime);
}
if(transactionTimeLeft > 0) {
transactionTimeLeft--;
}else if(!line.isEmpty()) {
Customer nextCustomer = line.dequeue();
transactionTimeLeft = nextCustomer.getTransactionTime()-1;
int timeWaited = clock - nextCustomer.getArrivalTime();
totalTimeWaited += timeWaited;
numberServed++;
System.out.println("Customer " + nextCustomer.getCustomerNumber() + " begins service at time " + clock + ". Time waited is " + timeWaited );
}
}
}
public void displayResults() {
System.out.println();
System.out.println("Number served = " + numberServed);
System.out.println("Total time Waited = " + totalTimeWaited);
double averageTimeWaited = ((double)totalTimeWaited) / numberServed;
System.out.println("Average time waited = " + averageTimeWaited);
int leftInLine = numberOfArrivals - numberServed;
System.out.println("Number left in line " + leftInLine);
}
}```
I am currently stuck on meeting the criteria described, I put the simulate function in a loop that looped 10 times and after that I used displayresults, but my results did not fit the criteria.
Any help is appreciated, thank you.
transactionTime is handled in the main method/simulation(s) and not on the customer instance itself. Yo just need the arrivalTime and departureTime, and the constructor only needs to be Customer( int arrives ). Then you just need getArrivalTime() setDepartureTime(int time) and totalTime()
Then use import java.util.*; with Queue<Customer> line = new LinkedList<Customer>() and from there you just need the main method using that queue and the customer class that I modified above.

load a world asynchronously or without blocking the main thread

I'm working on a minigames plugin. After an arena finishes it should be regenerated - I use the unload and load trick. It has an obvious disadvantage - it freezes the server for a while to prepare spawn areas. I decided to put the arena reset code into an runnable asynchronous task runTaskAsynchronously(). However, when the server tries to run the code inside the thread, it throws an exception:
Caused by: java.lang.IllegalStateException: Asynchronous entity world add!
Here's a part of my code:
getServer().getScheduler().runTaskAsynchronously(this, new Runnable()
{
#Override
public void run()
{
String w_name = world.getName();
getServer().unloadWorld(world.getName(), false);
world = getServer().createWorld(new WorldCreator(w_name));
}
});
Any suggestions how to deal with this problem?
Bukkit doesn't like it when you try to edit anything through the API in an async task. Reading and processing is fine but bukkit enforces nothing when it comes to thread safety and thus affecting the world with more than 1 thread can cause issues.
Try splitting up your arena reset into smaller chunks and spreading out the operation over several ticks with a series of synchronous tasks, might help with the performance.
This isn't my code but it does a decent job of demonstrating the idea https://gist.github.com/aadnk/5443172
-- You can load worlds async using this: http://pastebin.com/K9CuVMS5 --
No you cannot, and if you tried it would have a high chance of world corruption. But if you don't care about it this is what you can do:
Bukkit loads worlds via Bukkit.createWorld(WorldCreator) which activates Server.createWorld(WorldCreator) which activates:
Validate.notNull(creator, "Creator may not be null");
String name = creator.name();
ChunkGenerator generator = creator.generator();
File folder = new File(this.getWorldContainer(), name);
World world = this.getWorld(name);
WorldType type = WorldType.getType(creator.type().getName());
boolean generateStructures = creator.generateStructures();
if(world != null) {
return world;
} else if(folder.exists() && !folder.isDirectory()) {
throw new IllegalArgumentException("File exists with the name \'" + name + "\' and isn\'t a folder");
} else {
if(generator == null) {
generator = this.getGenerator(name);
}
WorldLoaderServer converter = new WorldLoaderServer(this.getWorldContainer());
if(converter.isConvertable(name)) {
this.getLogger().info("Converting world \'" + name + "\'");
converter.convert(name, new ConvertProgressUpdater(this.console));
}
int dimension = 10 + this.console.worlds.size();
boolean used = false;
do {
Iterator sdm = this.console.worlds.iterator();
while(sdm.hasNext()) {
WorldServer hardcore = (WorldServer)sdm.next();
used = hardcore.dimension == dimension;
if(used) {
++dimension;
break;
}
}
} while(used);
boolean var25 = false;
ServerNBTManager var24 = new ServerNBTManager(this.getWorldContainer(), name, true);
WorldData worlddata = var24.getWorldData();
if(worlddata == null) {
WorldSettings internal = new WorldSettings(creator.seed(), EnumGamemode.getById(this.getDefaultGameMode().getValue()), generateStructures, var25, type);
internal.setGeneratorSettings(creator.generatorSettings());
worlddata = new WorldData(internal, name);
}
worlddata.checkName(name);
WorldServer var26 = (WorldServer)(new WorldServer(this.console, var24, worlddata, dimension, this.console.methodProfiler, creator.environment(), generator)).b();
if(!this.worlds.containsKey(name.toLowerCase())) {
return null;
} else {
var26.scoreboard = this.getScoreboardManager().getMainScoreboard().getHandle();
var26.tracker = new EntityTracker(var26);
var26.addIWorldAccess(new WorldManager(this.console, var26));
var26.worldData.setDifficulty(EnumDifficulty.EASY);
var26.setSpawnFlags(true, true);
this.console.worlds.add(var26);
if(generator != null) {
var26.getWorld().getPopulators().addAll(generator.getDefaultPopulators(var26.getWorld()));
}
this.pluginManager.callEvent(new WorldInitEvent(var26.getWorld()));
System.out.print("Preparing start region for level " + (this.console.worlds.size() - 1) + " (Seed: " + var26.getSeed() + ")");
if(var26.getWorld().getKeepSpawnInMemory()) {
short short1 = 196;
long i = System.currentTimeMillis();
for(int j = -short1; j <= short1; j += 16) {
for(int k = -short1; k <= short1; k += 16) {
long l = System.currentTimeMillis();
if(l < i) {
i = l;
}
if(l > i + 1000L) {
int chunkcoordinates = (short1 * 2 + 1) * (short1 * 2 + 1);
int j1 = (j + short1) * (short1 * 2 + 1) + k + 1;
System.out.println("Preparing spawn area for " + name + ", " + j1 * 100 / chunkcoordinates + "%");
i = l;
}
BlockPosition var27 = var26.getSpawn();
var26.chunkProviderServer.getChunkAt(var27.getX() + j >> 4, var27.getZ() + k >> 4);
}
}
}
this.pluginManager.callEvent(new WorldLoadEvent(var26.getWorld()));
return var26.getWorld();
}
}
Now by creating you own world loader, you can make it so it only generates a chunk every tick or so.

Splitting work between threads in a parameter range

I have a method that counts the prime numbers from 1 to x and when x is a really large number like 1000000000, I would like to split the work between threads. My prime counter accepts 1 parameter, x, that is the last number to check whether it is prime or not. This method returns the count, integer of primes in which the method calculated. I know that I can have something like:
Thread thread1 = new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
int primes = primeCounter(1000000);
System.out.println("There are: " + primes+ " primes in betweem 1 and " + 1000000);
}
});
But how can I continue from 1000000 to 100000000 with another thread and join those two threads?
First, like mentioned in the comment, change your primecounter so that ist accepts a range.
Second, create a class which extends Thread, e.g:
class MyThread extends Thread {
long start;
long stop;
int primes = 0;
MyThread(long start, long stop) {
this.start = start;
this.stop = stop;
}
#Override
public void run() {
primes = primeCounter(start, stop);
System.out.println("There are: " + primes + " primes in betweem " + start + " and " + stop);
}
private int primeCounter(long start, long stop) {
int counter = 0;
//.....
return counter;
}
}
Then you can create your threads, starting them and waiting until they are all finished:
long max = 1000000000;
long packetSize = 1000000;
List<MyThread> threads = new ArrayList<>();
long start = 0;
for (int i = 0; i < (int) (max / packetSize); i++) {
threads.add(new MyThread(start, start = start + packetSize));
}
threads.forEach((Thread t)-> t.start());
threads.forEach((Thread t)-> { try {t.join();}catch(InterruptedException e) {throw new RuntimeException(e);}});
The example is very simple, change it to your needs and don't forget the exception handling :)

Threading Isues With Fixed Thread Pool and Large Number of Tasks

I'm using a program to run the Collatz Conjecture (http://en.wikipedia.org/wiki/Collatz_conjecture) from mathematics. I've implemented a class that runs the conjecture algorithm (and gives you back the output) and one that creates a fixed thread pool (with my number of processors: 8) and accepts Callables which are calls for the conjecture algorithm.
I created a HashSet<Callable> for all the numbers between 1 (the input type must be a positive integer) and 400,000. This hangs (seemingly) forever, but lower numbers work out just fine, which is strange. Stranger yet, running it appears to take longer to process these calls than it takes a single thread to process the same amount of information; it also bloats the memory significantly.
For instance, on my computer, the program takes less than a second to perform the algorithm (just one iteration) with 400,000 (the final value) and all the lower values take less time to compute (maybe with the exception of primes, which take longer) I'm running Windows 8.1 with 8GB ram, and 8 logical processors at 2.2Ghz.
Code:
private static void initThreads() throws InterruptedException {
//Files.createDirectories(SEQUENCER_FOLDER_PATH);
//Files.createFile(SEQUENCER_FILE_PATH);
ExecutorService service = Executors.newFixedThreadPool(8, new ThreadFactory() {
private BigInteger count = BigInteger.ZERO;
#Override
public Thread newThread(Runnable r) {
count = count.add(BigInteger.ONE);
return new Thread(r, "Collatz Sequencer Thread: " + count);
}
});
int finalNumber = 400_000;
final HashSet<Callable<Void>> tasks = new HashSet<>(finalNumber);
for (long l = 1; l <= finalNumber; l++) {
final BigInteger number = BigInteger.valueOf(l);
tasks.add(() -> {
CollatzSequencer sequencer = new CollatzSequencer(new BigInteger(number.toString()));
synchronized (dataSet) {
dataSet.put(number, sequencer.init());
}
return null;
});
}
service.invokeAll(tasks);
Thread dataThread = new Thread(() -> {
while (true) {
synchronized (dataSet) {
if (dataSet.size() == finalNumber) {
System.err.println("Values: \n");
for (CollatzSequencer.FinalSequencerReport data : dataSet.values()) {
System.err.println("Entry: " + data.getInitialValue() + ", " + data.getIterations());
}
System.exit(0);
}
}
}
}, "Collatz Conjecture Data Set Thread");
dataThread.start();
}
Collatz Conjecture Algorithm:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.collatzsequencer.core;
import java.math.BigInteger;
/**
* A sequencer used for computing the collatz sequence.
*
* #author Sarah Szabo
* #version 1.0
*/
public class CollatzSequencer {
private final BigInteger initialValue;
public CollatzSequencer(BigInteger currentValue) {
if (currentValue == null) {
throw new NullPointerException("Value passed can't be null");
} else if (currentValue.compareTo(new BigInteger("1")) < 0) {
throw new NumberFormatException("The value passed to the constructor must be a natural number.");
}
this.initialValue = currentValue;
}
public FinalSequencerReport init() {
return new FinalSequencerReport(performOperation(new SequencerReport(this.initialValue)), this.initialValue);
}
private SequencerReport performOperation(SequencerReport report) {
if (report.getResult().equals(new BigInteger("1"))) {
return new SequencerReport(report.getResult(), report.getIterations(), report.getSequence().length() > 1
? report.getSequence().substring(0, report.getSequence().length() - 3) : "The sequence starts and ends at 1 <Nothing Done>");
} else if (report.getResult().mod(new BigInteger("2")).equals(new BigInteger("0"))) {
BigInteger value = report.getResult().divide(new BigInteger("2"));
return performOperation(new SequencerReport(value, report.getIterations().add(new BigInteger("1")),
report.getSequence() + " " + report.getResult() + "/2 -> " + value + " ->"));
} else {
BigInteger value = report.getResult().multiply(new BigInteger("3")).add(new BigInteger("1"));
return performOperation(new SequencerReport(value, report.getIterations()
.add(new BigInteger("1")), report.getSequence() + report.getResult() + " * 3 + 1 ->" + value + " ->"));
}
}
public static final class FinalSequencerReport extends SequencerReport {
private final BigInteger initialValue;
private final String finalFormattedString;
public FinalSequencerReport(SequencerReport finalReport, BigInteger initialValue) {
super(finalReport.getResult(), finalReport.getIterations(), finalReport.getSequence());
this.initialValue = initialValue;
this.finalFormattedString = "Initial Value: "
+ getInitialValue() + "\nFinal Value: " + getResult() + "\nIterations: "
+ getIterations() + "\nAlgebraic Sequence:\n" + getSequence();
}
public String getFinalFormattedString() {
return finalFormattedString;
}
public BigInteger getInitialValue() {
return initialValue;
}
}
public static class SequencerReport {
private final BigInteger result, iterations;
private final String sequence;
public SequencerReport(BigInteger result) {
this(result, new BigInteger("0"), "");
}
public SequencerReport(BigInteger result, BigInteger iterations, String sequence) {
this.result = result;
this.iterations = iterations;
this.sequence = sequence;
}
public BigInteger getResult() {
return this.result;
}
public BigInteger getIterations() {
return this.iterations;
}
public String getSequence() {
return this.sequence;
}
}
}
As you said, your code works; the problem is probably just performance. Some things I would try:
Use long instead of BigInteger. BigInteger is very slow.
Instead of mod 2 (or % 2), use & 1. The binary AND will have effectively the same result and is much faster.
You are doing way, way too much String manipulation. Override sequencerReport.toString() and have it do the toString calls all at the end when you're printing the data.
Don't do new ThreadFactory(). Use Guava's ThreadFactoryBuilder.
You should never call new Thread() ever in your code unless you really know what you're doing, which means don't do it.
Add a wait/notify mechanism for dataThread instead of a busy loop. Call dataSet.notify() when the work is done and dataSet.wait() inside the dataThread body.

Getting familiar with Threads in Java: Why does this program's runtime increase with increasing number of threads

Situation
I am trying to get familiar with threads in Java. For that reason I modified a program listing I found in a book. What is does is pretty simple:
It creates a boolean[]-array with 100.000.000 elements.
It randomly fills that array's elements with true or false using NUMBER_OF_SERVERS threads.
Finally it scans that array with NUMBER_OF_SERVERS threads and counts how many entries are set to true
For further details, please see the code below on the bottom of this post.
Problem
When I run the code with different number of threads and measure the runtime, I get a very strange result; or at least a behaviour that I do not understand: The BuildService-Thread consumes MORE runtime when I use MORE threads. When building the entire array (based on random truedistribution) in just one thread that takes about 10 seconds. Next, when I use four threads I would expect the runtime to decrease. However, I get a time consumption of about 17 seconds.
My ScanService works as expected: Time consumption decreases with more threads.
Please see the following chart for details:
However, if change one line in my code and replace the if ((int) ((Math.random() * 2d)) == 0)-statement (for random true-distribution) with if (i % 2 == 0) (thus, every second item will be true) I get a behaviour I would expect:
Questions
So, my questions are:
Why do MORE threads lead to a LONGER runtime when using Math.random() function?
Vice versa, why does the runtime DECREASE when using only ONE thread using the exact same function?
What "general rules" can be derived from this behaviour when it comes down to dealing with threads?
Background info
The code was run on an Intel core i3.
Code
public class AsynchService
{
private static final int ARRAY_SIZE = 100000000; //100.000.000
private static final int NUMBER_OF_SERVERS = 16;
private static final int HOW_MANY = ARRAY_SIZE / NUMBER_OF_SERVERS;
//build array asynch
public static boolean[] buildArrayAsynch()
{
//build array with NUMBER_OF_SERVERS-Threads
boolean[] array = new boolean[ARRAY_SIZE];
Thread[] buildServerThread = new Thread[NUMBER_OF_SERVERS];
long startTime = System.currentTimeMillis();
for (int i = 0; i < NUMBER_OF_SERVERS; i++)
{
int start = i * HOW_MANY;
int end = (i != NUMBER_OF_SERVERS - 1) ? (i + 1) * HOW_MANY - 1 : ARRAY_SIZE - 1;
buildServerThread[i] = new BuildService(array, i, start, end);
}
//synchronize and wait for result
int expectedResult = 0;
for (int i = 0; i < NUMBER_OF_SERVERS; i++)
{
try
{
buildServerThread[i].join();
}
catch (InterruptedException ex) {}
expectedResult += ((BuildService) buildServerThread[i]).getExpectedResult();
}
System.out.println("\nNumber of \"true\"s ==> Expected result: " + expectedResult);
System.out.println("Build duration: " + (System.currentTimeMillis() - startTime) + " ms\n");
return array;
}
//scan array asynch
public static int scanArrayAsynch(boolean[] array)
{
//create services and server-threads
Thread[] serverThread = new Thread[NUMBER_OF_SERVERS];
long startTime = System.currentTimeMillis();
for (int i = 0; i < NUMBER_OF_SERVERS; i++)
{
int start = i * HOW_MANY;
int end = (i != NUMBER_OF_SERVERS - 1) ? (i + 1) * HOW_MANY - 1 : ARRAY_SIZE - 1;
serverThread[i] = new ScanService(array, i, start, end);
}
//synchronize with servers, wait for server end
int result = 0;
for (int i = 0; i < NUMBER_OF_SERVERS; i++)
{
try
{
serverThread[i].join();
}
catch (InterruptedException ex) {}
result += ((ScanService) serverThread[i]).getResult();
}
System.out.println("Search duration: " + (System.currentTimeMillis() - startTime) + " ms");
return result;
}
public static void main(String[] args)
{
//build array
boolean[] array = buildArrayAsynch();
//scan array
int result = scanArrayAsynch(array);
//display result
System.out.println("\nResult: " + result);
}
}
class BuildService extends Thread
{
private boolean[] array;
private int start;
private int end;
private int expectedResult = 0;
public BuildService(boolean[] array, int serviceId, int start, int end)
{
this.array = array;
this.start = start;
this.end = end;
this.setName("BuildService " + serviceId);
this.start();
}
public int getExpectedResult()
{
return expectedResult;
}
public void run()
{
if (start < 0 || end >= array.length) throw new IndexOutOfBoundsException();
System.out.println(getName() + ": StartIndex = " + start + "; EndIndex = " + end);
long startTime = System.currentTimeMillis();
for (int i = start; i <= end; i++)
{
//if (i % 2 == 0)
if ((int) ((Math.random() * 2d)) == 0)
{
array[i] = true;
expectedResult++;
}
else
{
array[i] = false;
}
}
System.out.println(getName() + " finished! \"true\" elements: " + expectedResult + "; duration = " + (System.currentTimeMillis() - startTime) + "ms");
}
}
class ScanService extends Thread
{
private boolean[] array;
private int serviceId;
private int start;
private int end;
private int result = 0;
public ScanService(boolean[] array, int serviceId, int start, int end)
{
this.array = array;
this.serviceId = serviceId;
this.start = start;
this.end = end;
this.start();
}
public int getResult()
{
return result;
}
public void run()
{
if (start < 0 || end >= array.length) throw new IndexOutOfBoundsException();
System.out.println("Server " + serviceId + ": StartIndex = " + start + "; EndIndex = " + end);
for (int i = start; i <= end; i++)
{
if (array[i]) result++;
}
}
}
The devil is in the details. The documentation of Math.random() has the answer:
This method is properly synchronized to allow correct use by more than one thread. However, if many threads need to generate pseudorandom numbers at a great rate, it may reduce contention for each thread to have its own pseudorandom-number generator.
To get around this issue, try creating an instance of java.util.Random for each instance of your BuildService class, and use that instead of Math.random(). A benefit of using java.util.Random is also that you don't have to do unnecessary double-int-arithmetic, but can simply use the nextBoolean() method.
You are probably seeing the effect of thread contention in Math.random(). Java 7 has the ThreadLocalRandom feature to avoid just this problem.
Your Code seems to be using 16 number of threads and each thread is using the Join method as in here
serverThread[i].join();
which seems to not use the full potential of threads.
when using the join you are actually saying the thread to wait till the other thread completes not running the threads in parallel.
you might want to use start method instead of join method.
Try running the changed code and post your analysis on time line.
Good luck learning
Taking Andreas Troelsen's answer into account I came up with the code shown below leading to the following runtimes.
Compared to what happened before, this solution now meets my expectations much better!
import java.util.Random;
class BuildService extends Thread
{
private boolean[] array;
private int start;
private int end;
private int expectedResult = 0;
private Random random = new Random();
public BuildService(boolean[] array, int serviceId, int start, int end)
{
this.array = array;
this.start = start;
this.end = end;
this.setName("BuildService " + serviceId);
this.start();
}
public int getExpectedResult()
{
return expectedResult;
}
public void run()
{
if (start < 0 || end >= array.length) throw new IndexOutOfBoundsException();
System.out.println(getName() + ": StartIndex = " + start + "; EndIndex = " + end);
long startTime = System.currentTimeMillis();
for (int i = start; i <= end; i++)
{
array[i] = random.nextBoolean();
if (array[i]) expectedResult++;
}
System.out.println(getName() + " finished! \"true\" elements: " + expectedResult + "; duration = " + (System.currentTimeMillis() - startTime) + "ms");
}
}

Categories

Resources