Java - Multithreads exercise - java

I have to solve an homework, this is the question:
Look this program, run it and found the problem then fix it.
this is the program:
package serie02;
import java.util.ArrayList;
class S2Es1Timer {
private long startTime = -1;
private long stopTime = -1;
final protected void start() {
startTime = System.currentTimeMillis();
}
final protected void stop() {
stopTime = System.currentTimeMillis();
}
final public long getElapsedTime() {
if (startTime < 0 || stopTime < 0)
return 0;
return stopTime - startTime;
}
}
class S2Es1SharedState {
boolean sharedState = false;
}
class S2Es1Worker implements Runnable {
private static final long START_VALUE = 1000000000;
private final int id;
private final S2Es1Timer timer;
private final S2Es1SharedState state;
private long localCounter = START_VALUE;
public S2Es1Worker(final int id, final S2Es1SharedState state) {
this.id = id;
timer = new S2Es1Timer();
this.state = state;
log("created");
}
#Override
public void run() {
timer.start();
while (--localCounter > 0) {
if (state.sharedState) {
timer.stop();
log("sharedState has already been set by another worker. localCounter: "
+ ((100.0 * localCounter) / START_VALUE)
+ " % from goal");
return;
}
}
// richiedi accesso esclusivo per scrittura
state.sharedState = true;
timer.stop();
log("sharedState has been set");
}
final protected void log(final String s) {
System.out.println(this.getClass().getSimpleName() + id + ": " + s);
}
final protected void logElapseTime() {
log("time: " + timer.getElapsedTime() + " ms");
}
}
public class S2Esercizio1 {
private final static int NUM_WORKERS = 10;
public static void main(final String[] args) {
final S2Es1Timer mainTimer = new S2Es1Timer();
final ArrayList<Thread> threads = new ArrayList<Thread>();
final ArrayList<S2Es1Worker> workers = new ArrayList<S2Es1Worker>();
final S2Es1SharedState myShare = new S2Es1SharedState();
// Crea 10 Workers
for (int i = 0; i < NUM_WORKERS; i++) {
final S2Es1Worker worker = new S2Es1Worker(i, myShare);
workers.add(worker);
threads.add(new Thread(worker));
}
System.out.println("Simulation started");
System.out.println("------------------------------------");
mainTimer.start();
// Fa partire tutte le threads
for (final Thread t : threads)
t.start();
try {
// Attende che tutte le threads terminano
for (final Thread t : threads)
t.join();
} catch (final InterruptedException e) {
/* Unhandled exception */
}
mainTimer.stop();
System.out.println("------------------------------------");
for (final S2Es1Worker worker : workers)
worker.logElapseTime();
System.out.println("Simulation time: " + mainTimer.getElapsedTime()
+ " ms");
System.out.println("Simulation finished");
}
}
this is the output:
S2Es1Worker0: created
S2Es1Worker1: created
S2Es1Worker2: created
S2Es1Worker3: created
S2Es1Worker4: created
S2Es1Worker5: created
S2Es1Worker6: created
S2Es1Worker7: created
S2Es1Worker8: created
S2Es1Worker9: created
Simulation started
------------------------------------
S2Es1Worker6: sharedState has been set
S2Es1Worker7: sharedState has been set
S2Es1Worker3: sharedState has been set
S2Es1Worker5: sharedState has been set
S2Es1Worker9: sharedState has been set
S2Es1Worker0: sharedState has been set
S2Es1Worker4: sharedState has been set
S2Es1Worker8: sharedState has been set
S2Es1Worker1: sharedState has been set
S2Es1Worker2: sharedState has been set
------------------------------------
S2Es1Worker0: time: 2300 ms
S2Es1Worker1: time: 2401 ms
S2Es1Worker2: time: 2420 ms
S2Es1Worker3: time: 1921 ms
S2Es1Worker4: time: 2332 ms
S2Es1Worker5: time: 2169 ms
S2Es1Worker6: time: 909 ms
S2Es1Worker7: time: 1847 ms
S2Es1Worker8: time: 2305 ms
S2Es1Worker9: time: 2237 ms
Simulation time: 2422 ms
Simulation finished
I think is a problem of visibility, i mean when the first thread finish the loop and set the shared mutable variable sharedState to true, the others threads continue to loop because they have a "dirty read" in the cache cpu (i have 4 cpu).
So the only good thing that i can do is set the sharedState variable with the key volatile. in this way every thread reads the correct value. I lost time because this value now is catched by the main memory instead of the cpu cache.
Another solution could be: to use ReentrantReadWriteLock.
So the class S2Es1SharedState could be writed in this way:
class S2Es1SharedState {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
boolean sharedState = false;
public boolean getSharedState() {
readLock.lock();
try {
return sharedState;
} finally {
readLock.unlock();
}
}
public void setSharedState() {
writeLock.lock();
try {
this.sharedState = true;
}
finally {
writeLock.unlock();
}
}
}
This solution i think work because the lock also give a correct visibility but i think take a lot of time because in this way inside the loop i don't use the cache cpu but just the main memory. and this operation need time.
I'm not sure about my analysis, for this reason i prefer to ask to you.

Related

How do I make a single-task FILO background thread?

I have a bunch of threads that spawn somewhat arbitrarily. When they are racing each other, only the one that spawned last is relevant. The other threads can be thrown away or stopped. But I am not sure how to do that, so I have implemented a very basic counter that checks whether the thread is the latest spawned thread.
edit: I would like to be able to kill threads that are taking too long (as they are no longer necessary); probably not from within the threads themselves as they are busy doing something else.
This code works, it seems. But it doesn't feel robust. Can someone give me a hint toward a proper way to do this?
class Main {
private static volatile int latestThread = 0;
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
spawnThread();
}
}
private static void spawnThread() {
latestThread++;
int thisThread = latestThread;
new Thread(() -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (latestThread == thisThread) {
// only the latest "active" thread is relevant
System.out.println("I am the latest thread! " + thisThread);
}
}).start();
}
}
output:
I am the latest thread! 10
code in replit.com
ThreadPoolExecutor is almost what I need, specifically DiscardOldestPolicy. You can set the queue size to 1, so one thread is running and one thread is in the queue, and the oldest in the queue just gets shunted. Clean!
But it finishes two threads (not only the latest), which is not 100% what I was looking for. Although arguably good enough:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class DiscardOldest {
private static int threadCounter = 1;
public static void main(String[] args) throws InterruptedException {
int poolSize = 0;
int maxPoolSize = 1;
int queueSize = 1;
long aliveTime = 1000;
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(queueSize);
ThreadPoolExecutor executor = new ThreadPoolExecutor(poolSize, maxPoolSize, aliveTime, TimeUnit.MILLISECONDS, queue, new ThreadPoolExecutor.DiscardOldestPolicy());
for (int i = 0; i < 4; i++) {
spawnThread(executor);
}
}
private static void spawnThread(ThreadPoolExecutor executor) {
final int thisThread = threadCounter++;
System.out.println(thisThread + " spawning");
executor.execute(() -> {
try {
Thread.sleep(100);
System.out.println(thisThread + " finished!");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
Ouput:
1 spawning
2 spawning
3 spawning
4 spawning
1 finished!
4 finished!
Rather than relaying on an index, a born time could be set. If there's a younger thread (was born later) the thread should terminate its execution.
public class Last {
private static volatile long latestThread = 0L;
/**
* #param args
*/
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
spawnThread(System.nanoTime(), i);
}
}
private static void spawnThread(long startTime, int index) {
new Thread(() -> {
latestThread = startTime;
long thisThread = startTime;
boolean die = false;
try {
while (!die) {
Thread.sleep(1);
if (thisThread < latestThread) {
System.out.println(
index + ": I am not the latest thread :-(\n\t" + thisThread + "\n\t" + latestThread);
die = true;
} else if (thisThread == latestThread) {
System.out.println(
index + ": Yes! This is the latest thread!\n\t" + thisThread + "\n\t" + latestThread);
Thread.sleep(1);
System.out.println("Bye!");
die = true;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
Result:
0: I am not the latest thread :-(
39667589567880
39667602317461
2: Yes! This is the latest thread!
39667602317461
39667602317461
1: I am not the latest thread :-(
39667602257160
39667602317461
Bye!
I did a little research based on comments from everybody (thanks!) and ThreadPoolExecutor is almost what I need, but I want a pool with the total size of 1 (no queue) that kills the active thread once a new thread comes along, which is not allowed in a thread pool and not in line with what a ThreadPool is for. So instead, I came up with a reference to the active thread, and when a new thread comes a long it kills the old one, which seems to do what I want:
import java.util.concurrent.atomic.AtomicInteger;
public class Interrupt {
private static final AtomicInteger CURRENT_THREAD = new AtomicInteger(0);
private static Thread activeThread = new Thread(() -> {});
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 4; i++) {
spawnThread();
Thread.sleep(3);
}
}
private static void spawnThread() {
if (activeThread.isAlive()) {
activeThread.interrupt();
}
activeThread = new Thread(() -> {
int thisThread = CURRENT_THREAD.incrementAndGet();
System.out.println(thisThread + " working");
try {
Thread.sleep(1000);
System.out.println(thisThread + " finished!");
} catch (InterruptedException ignored) {}
});
activeThread.start();
}
}
Output:
3 working
2 working
1 working
4 working
4 finished!

Why will the CPU time be different without using volatile?

Code 1: In this code, I use the System.out.println(); to get the value changed by other thread instead of using volatile.
package edu.seu.juc.vol;
import edu.seu.juc.annotation.ThreadNotSafe;
import java.lang.management.ManagementFactory;
/**
* #author: zs.sun
* Create at: 2020/5/24 10:31
* #Package: edu.seu.juc.vol
* #ProjectName: af-study
* #Description:
*/
#ThreadNotSafe
public class TestVolatile01 {
public static void main(String[] args) {
Long startTime = System.currentTimeMillis();
ThreadDemo td = new ThreadDemo();
new Thread(td).start();
while (true) {
if (td.isFlag()) {
System.out.println("--------------------------");
break;
}
System.out.println(); // to see the value changed by other thread
}
System.out.println("total: " + (System.currentTimeMillis() - startTime));
System.out.println("CPU time: " + ManagementFactory.getThreadMXBean().getThreadCpuTime(Thread.currentThread().getId())
/ (1000 * 1000));
}
private static class ThreadDemo implements Runnable {
private boolean flag = false;
#Override
public void run() {
try {
Thread.sleep(2000);
flag = true;
System.out.println("flag = " + isFlag());
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
}
Result1
...(some blank lines)
flag = true
--------------------------
total: 2010
CPU time: 875
Code 2: In this code, I use the volatile to get the value changed by other thread.
package edu.seu.juc.vol;
import edu.seu.juc.annotation.ThreadSafe;
import java.lang.management.ManagementFactory;
/**
* #author: zs.sun
* Create at: 2020/5/24 10:31
* #Package: edu.seu.juc.vol
* #ProjectName: af-study
* #Description:
*/
#ThreadSafe
public class TestVolatile02 {
public static void main(String[] args) {
Long startTime = System.currentTimeMillis();
ThreadDemo td = new ThreadDemo();
new Thread(td).start();
while (true) {
if (td.isFlag()) {
System.out.println("--------------------------");
break;
}
}
System.out.println("total: " + (System.currentTimeMillis() - startTime));
System.out.println("CPU time: " + ManagementFactory.getThreadMXBean().getThreadCpuTime(Thread.currentThread().getId())
/ (1000 * 1000));
}
private static class ThreadDemo implements Runnable {
private volatile boolean flag = false;
#Override
public void run() {
try {
Thread.sleep(2000);
flag = true;
System.out.println("flag = " + isFlag());
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
}
Result 2
--------------------------
total: 2005
flag = true
CPU time: 1968
Can anyone tell me why the CPU time is different?
In my opinion, the main thread in both Codes keeps the CPU all the time, but the Code 1 keeps the absolutely less CPU time.
In Code 1, time spent in the OS Kernel to print the blank line (and maybe scroll the terminal window), is time not spent in the Java thread, so not all the CPU time is counted when printing.
The loop in Code 2 is a pure CPU loop, so all the time is spent by the Java thread.

Java looping Threads using CyclicBarrier

I have a program with this general structure:
init
create CyclicBarrier
initialise all threads, attaching to barrier
*start all threads*
wait for join
display stats
*start all threads*
perform calculation
await barrier
My problem is I need the threads' run() method to keep looping until a certain condition is met, but pausing after every iteration to let all threads synchronise.
I've already tried attaching a Runnable method to the barrier, but this ends up requiring the recreation and restarting of each thread, which isn't a very good solution.
I've also tried using the CyclicBarrier's reset() method, but this just seems to cause errors on the existing threads, even when executed after all threads have completed.
My question is:
-Is it possible to 'reset' a barrier and have all the barrier's threads follow the same conditions as they did before the first invocations of await()?
-Or is there another method I should be using to achieve this?
Thanks in advance
The barrier.wait() will suspend the threads. The barrier is already in the main thread, it does not need another. In your algorithm above you show the threads being restarted after displaying stats. You should not need to do this. If the recently awakened threads are in a loop they will go back into the barrier.wait() again.
Following #Totoro's answer, below is a little bit of example code which also incorporates the requirement "I need the threads' run() method to keep looping until a certain condition is met, pausing after every iteration to let all threads synchronise". That makes it complex pretty quick, but hopefully the program output will clarify the example code (or I should just make better examples).
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class BarrierCalc implements Runnable {
public static final int CALC_THREADS = 3;
private static final AtomicBoolean runCondition = new AtomicBoolean();
private static final AtomicBoolean stopRunning = new AtomicBoolean();
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(CALC_THREADS + 1);
for (int i = 0; i < CALC_THREADS; i++) {
new Thread(new BarrierCalc(barrier)).start();
}
try {
runCondition.set(true);
barrier.await();
showln(0, "STATS!");
barrier.await();
showln(0, "start looping 1");
Thread.sleep(200);
runCondition.set(false);
showln(0, "stop looping 1");
barrier.await();
runCondition.set(true);
barrier.await();
showln(0, "start looping 2");
Thread.sleep(100);
runCondition.set(false);
showln(0, "stop looping 2");
barrier.await();
stopRunning.set(true);
showln(0, "finishing");
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
private static final AtomicInteger calcId = new AtomicInteger();
private CyclicBarrier barrier;
private int id;
public BarrierCalc(CyclicBarrier barrier) {
this.barrier = barrier;
id = calcId.incrementAndGet();
}
public void run() {
showln(id, "waiting for start");
try {
barrier.await(); // display stats
barrier.await(); // start running
int loopNumber = 0;
while (!stopRunning.get()) {
showln(id, "looping " + (++loopNumber));
while (runCondition.get()) {
Thread.sleep(10); // simulate looping
}
showln(id, "synchronizing " + loopNumber);
barrier.await();
showln(id, "synchronized " + loopNumber);
// give main thread a chance to set stopCondition and runCondition
barrier.await();
}
showln(id, "finished");
} catch (Exception e) {
e.printStackTrace();
}
}
private static final long START_TIME = System.currentTimeMillis();
public static void showln(int id, String msg) {
System.out.println((System.currentTimeMillis() - START_TIME) + "\t ID " + id + ": " + msg);
}
}
Keep in mind that program output might not be in the order expected: threads that are writing at the same time to one synchronized output (System.out) are given write-access in random order.
You can take a look at my example where I played with CyclicBarrier.Here each worker makes some calculation and at the barrier the condition is checked. If it meets the condition than all workers stop calculations, otherwise they continue:
class Solver {
private static final int REQUIRED_AMOUNT = 100;
private static final int NUMBER_OF_THREADS = 4;
AtomicInteger atomicInteger = new AtomicInteger();
AtomicBoolean continueCalculation = new AtomicBoolean(true);
final CyclicBarrier barrier;
public static void main(String[] args) {
new Solver();
}
class Worker implements Runnable {
int workerId;
Worker(int workerId) {
this.workerId = workerId;
}
public void run() {
try {
while(continueCalculation.get()) {
calculate(workerId);
barrier.await();
}
} catch (Exception ex) {
System.out.println("Finishing " + workerId);
}
}
}
public Solver() {
Runnable barrierAction = () -> {
if (done()) {
continueCalculation.set(false);
}
};
barrier = new CyclicBarrier(NUMBER_OF_THREADS, barrierAction);
List<Thread> threads = new ArrayList(NUMBER_OF_THREADS);
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
Thread thread = new Thread(new Worker(i));
threads.add(thread);
thread.start();
}
}
private void calculate(int workerId) throws InterruptedException {
// Some long-running calculation
Thread.sleep(2000L);
int r = new Random().nextInt(12);
System.out.println("Worker #" + workerId + " added " + r +" = " + atomicInteger.addAndGet(r));
}
private boolean done() {
int currentResult = atomicInteger.get();
boolean collected = currentResult >= REQUIRED_AMOUNT;
System.out.println("=======================================================");
System.out.println("Checking state at the barrier: " + currentResult);
if (collected) {
System.out.println("Required result is reached");
}
System.out.println("=======================================================");
return collected;
}
}

How do we know threadPoolExecutor has finished execution

I have a parent thread that sends messages to MQ and it manages a ThreadPoolExecutor for worker threads which listen to MQ and writes message to output file. I manage a threadpool of size 5. So when I run my program, I have 5 files with messages. Everything works fine until here. I now need to merge these 5 files in my parent thread.
How do I know ThreadPoolExecutor finished processing so I can start merging files.
public class ParentThread {
private MessageSender messageSender;
private MessageReciever messageReciever;
private Queue jmsQueue;
private Queue jmsReplyQueue;
ExecutorService exec = Executors.newFixedThreadPool(5);
public void sendMessages() {
System.out.println("Sending");
File xmlFile = new File("c:/filename.txt");
List<String> lines = null;
try {
lines = FileUtils.readLines(xmlFile, null);
} catch (IOException e) {
e.printStackTrace();
}
for (String line : lines){
messageSender.sendMessage(line, this.jmsQueue, this.jmsReplyQueue);
}
int count = 0;
while (count < 5) {
messageSender.sendMessage("STOP", this.jmsQueue, this.jmsReplyQueue);
count++;
}
}
public void listenMessages() {
long finishDate = new Date().getTime();
for (int i = 0; i < 5; i++) {
Worker worker = new Worker(i, this.messageReciever, this.jmsReplyQueue);
exec.execute(worker);
}
exec.shutdown();
if(exec.isTerminated()){ //PROBLEM is HERE. Control Never gets here.
long currenttime = new Date().getTime() - finishDate;
System.out.println("time taken: "+currenttime);
mergeFiles();
}
}
}
This is my worker class
public class Worker implements Runnable {
private boolean stop = false;
private MessageReciever messageReciever;
private Queue jmsReplyQueue;
private int processId;
private int count = 0;
private String message;
private File outputFile;
private FileWriter outputFileWriter;
public Worker(int processId, MessageReciever messageReciever,
Queue jmsReplyQueue) {
this.processId = processId;
this.messageReciever = messageReciever;
this.jmsReplyQueue = jmsReplyQueue;
}
public void run() {
openOutputFile();
listenMessages();
}
private void listenMessages() {
while (!stop) {
String message = messageReciever.receiveMessage(null,this.jmsReplyQueue);
count++;
String s = "message: " + message + " Recieved by: "
+ processId + " Total recieved: " + count;
System.out.println(s);
writeOutputFile(s);
if (StringUtils.isNotEmpty(message) && message.equals("STOP")) {
stop = true;
}
}
}
private void openOutputFile() {
try {
outputFile = new File("C:/mahi/Test", "file." + processId);
outputFileWriter = new FileWriter(outputFile);
} catch (IOException e) {
System.out.println("Exception while opening file");
stop = true;
}
}
private void writeOutputFile(String message) {
try {
outputFileWriter.write(message);
outputFileWriter.flush();
} catch (IOException e) {
System.out.println("Exception while writing to file");
stop = true;
}
}
}
How will I know when the ThreadPool has finished processing so I can do my other clean up work?
Thanks
If you Worker class implements Callable instead of Runnable, then you'd be able to see when your threads complete by using a Future object to see if the Thread has returned some result (e.g. boolean which would tell you whether it has finished execution or not).
Take a look in section "8. Futures and Callables" # website below, it has exactly what you need imo:
http://www.vogella.com/articles/JavaConcurrency/article.html
Edit: So after all of the Futures indicate that their respective Callable's execution is complete, its safe to assume your executor has finished execution and can be shutdown/terminated manually.
Something like this:
exec.shutdown();
// waiting for executors to finish their jobs
while (!exec.awaitTermination(50, TimeUnit.MILLISECONDS));
// perform clean up work
You can use a thread for monitoring ThreadPoolExecutor like that
import java.util.concurrent.ThreadPoolExecutor;
public class MyMonitorThread implements Runnable {
private ThreadPoolExecutor executor;
private int seconds;
private boolean run=true;
public MyMonitorThread(ThreadPoolExecutor executor, int delay)
{
this.executor = executor;
this.seconds=delay;
}
public void shutdown(){
this.run=false;
}
#Override
public void run()
{
while(run){
System.out.println(
String.format("[monitor] [%d/%d] Active: %d, Completed: %d, Task: %d, isShutdown: %s, isTerminated: %s",
this.executor.getPoolSize(),
this.executor.getCorePoolSize(),
this.executor.getActiveCount(),
this.executor.getCompletedTaskCount(),
this.executor.getTaskCount(),
this.executor.isShutdown(),
this.executor.isTerminated()));
try {
Thread.sleep(seconds*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
And add
MyMonitorThread monitor = new MyMonitorThread(executorPool, 3);
Thread monitorThread = new Thread(monitor);
monitorThread.start();
to your class where ThreadPoolExecutor is located.
It will show your threadpoolexecutors states in every 3 seconds.

Application hangs for few minutes even after all the threads are done

I converted a working Producer/Consumer Example from Thread/Runnable to Executor/Callable/BlockingQueues and using the Poison Pill termination pattern.
If you run the program below, it will hang for few minutes even though every thread has completed.
jstack shows numerous threads blocked on a queue that is not seemingly related to the application.
"pool-1-thread-10" prio=5 tid=10b08d000 nid=0x10d91c000 waiting on condition [10d91b000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <7f3113510> (a java.util.concurrent.SynchronousQueue$TransferStack)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:198)
at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:424)
at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:323)
at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:874)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:945)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:680)
I can not figure out why the application hangs. Any help is appreciated.
Thank you
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
public class ProducersConsumers {
private LinkedBlockingQueue<Item> queue = new LinkedBlockingQueue<Item>();
private static final ExecutorService executorPool = Executors.newCachedThreadPool();
private Random randGenerator = new Random(System.currentTimeMillis());
private class Item {
private boolean done = false;
private String message;
private Item(boolean done) {
this.done = done;
}
private Item(String message) {
this.message = message;
}
public boolean isDone() {
return done;
}
public String getMessage() {
return message;
}
}
private class Producer implements Callable<Long> {
private final int id;
private Integer numOfMessages;
private Producer(int id, int numOfMessages) {
this.id = id;
this.numOfMessages = numOfMessages;
}
#Override
public Long call() throws Exception {
long totalTime = 0;
while (numOfMessages > 0) {
String message;
synchronized (numOfMessages) {
long starttime = System.nanoTime();
int msgLength = randGenerator.nextInt(20000);
StringBuilder sb = new StringBuilder(msgLength);
for (int a = 0; a < msgLength; a++) {
sb.append((char) ('a' + randGenerator.nextInt(26)));
}
message = sb.toString();
long endtime = System.nanoTime();
totalTime += endtime - starttime;
}
numOfMessages--;
queue.put(new Item(message));
}
System.out.println("-------------Producer " + id + " is done.");
queue.put(new Item(true));
return totalTime;
}
}
private class Consumer implements Callable<Long> {
private String monitor = "monitor";
private final int id;
private Consumer(int id) {
this.id = id;
}
#Override
public Long call() throws Exception {
long totalTime = 0;
while (true) {
Item item = queue.take();
if (item.isDone()) {
break;
}
synchronized (monitor) {
long starttime = System.nanoTime();
StringBuilder sb = new StringBuilder(item.getMessage());
sb = sb.reverse();
String message = sb.toString();
long endtime = System.nanoTime();
totalTime += endtime - starttime;
}
}
System.out.println("+++++++++++++Consumer " + id + " is done.");
return totalTime;
}
}
public void begin(int threadCount) throws InterruptedException, ExecutionException {
Collection<Producer> producers = new ArrayList<Producer>();
for (int i = 0; i < threadCount; i++) {
producers.add(new Producer(i, randGenerator.nextInt(5)));
}
Collection<Consumer> consumers = new ArrayList<Consumer>();
for (int i = 0; i < threadCount; i++) {
consumers.add(new Consumer(i));
}
try {
long starttime = System.nanoTime();
List<Future<Long>> producerFutureList = executorPool.invokeAll(producers);
List<Future<Long>> consumerFutureList = executorPool.invokeAll(consumers);
long producerTotalTime = 0;
long consumerTotalTime = 0;
for (Future<Long> future : producerFutureList) {
producerTotalTime += future.get();
}
for (Future<Long> future : consumerFutureList) {
consumerTotalTime += future.get();
}
long mainThreadTotalTime = System.nanoTime() - starttime;
System.out.println("producerTotalTime " + producerTotalTime);
System.out.println("consumerTotalTime " + consumerTotalTime);
System.out.println("mainThreadTotalTime " + mainThreadTotalTime);
System.out.println("Difference " + (producerTotalTime + consumerTotalTime - mainThreadTotalTime));
} catch (InterruptedException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
throw e;
} catch (ExecutionException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
throw e;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ProducersConsumers prodcon = new ProducersConsumers();
prodcon.begin(20);
}
}
You should close the ExecutorService when you are done with it. Call executorPool.shutdown() at the end of your program.
You seem to be using a shared resource, specifically numOfMessages outside of a synchronized block.
while (numOfMessages > 0) {
// blah
synchronized (numOfMessages) {
// blah
}
}
I don't think this is the cause of your problem, but it's certainly non thread-safe. It's a typical check-then-act scenario. Refer to either Java Concurrency in Practice or Effective Java for reasons why this is Not Good (TM).

Categories