Is my producer/consumer solution correct? - java

I'm trying to learn more about threads and thought that coming up with a solution to the producer/consumer problem would be a good start. One of the constraints I put on the solution was that the consumer does not know ahead of time how much the producer is producing. The code runs as expected and I've run it many many times, but that doesn't mean that it is free of flaws. Are there any problems with this solution?
package Multithreading.ProducerConsumer;
import java.util.LinkedList;
import java.util.concurrent.Semaphore;
public class ProducerConsumer
{
private class Producer implements Runnable
{
#Override
public void run()
{
for(int i = 0; i < 1000; i++)
{
try
{
canProduce.acquire();
mutex.acquire();
queue.add(i);
mutex.release();
canConsume.release();
}
catch (InterruptedException ex)
{
;
}
}
try
{
canConsume.acquire();
isTryingToFinish = true;
canConsume.release();
}
catch (InterruptedException ex)
{
;
}
}
}
private class Consumer implements Runnable
{
#Override
public void run()
{
while(!isDone)
{
try
{
canConsume.acquire();
mutex.acquire();
System.out.println(queue.pop());
if(isTryingToFinish && queue.isEmpty())
{
isDone = true;
}
mutex.release();
canProduce.release();
}
catch (InterruptedException ex)
{
;
}
}
}
}
Semaphore canProduce;
Semaphore canConsume;
Semaphore mutex;
boolean isTryingToFinish = false;
boolean isDone = false;
final static int bufferSize = 100;
LinkedList<Integer> queue;
public ProducerConsumer()
{
queue = new LinkedList<>();
canProduce = new Semaphore(bufferSize);
canConsume = new Semaphore(0);
mutex = new Semaphore(1);
}
public void Go() throws InterruptedException
{
Thread p = new Thread(new Producer());
Thread c = new Thread(new Consumer());
p.start();
c.start();
p.join();
c.join();
System.out.println("Job Complete!");
}
public static void main(String[] args) throws InterruptedException
{
ProducerConsumer p = new ProducerConsumer();
p.Go();
}
}

You could look at MSDN's 'Example 2: Synchronizing two threads: a producer and a consumer'. It's c# but that should not be a problem.

Related

Consumer not exiting when using ExecutorService.submit

I am trying to implement a small producer-consumer example in Java using ExecutorService.
Here is my main class
class Example {
public static void main(String args[]) {
BlockingQueue<String> queue = new ArrayBlockingQueue<>(1000);
Producer producer = new Producer(queue);
Consumer consumer = new Consumer(queue);
ExecutorService executor = Executors.newCachedThreadPool();
// executor.execute(consumer);
Future producerFuture = executor.submit(producer);
Future consumerFuture = executor.submit(consumer);
try {
producerFuture.get();
consumerFuture.get();
} catch (InterruptedException e) {
LOG.error("Failed");
}
executor.shutdown();
executor.awaitTermination(10, TimeUnit.MILLISECONDS);
}
}
Producer Class
public class Producer implements Runnable {
private BlockingQueue<String> queue;
public Producer(BlockingQueue<String> queue) {
this.queue = queue;
}
#Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
queue.put(i + "HELPPPPP");
} catch (InterruptedException ex) {
Logger.getLogger(MigrationToolProducer.class.getName()).log(Level.SEVERE, null, ex);
}
}
Consumer Class
public class Consumer implements Runnable {
private final BlockingQueue<String> queue;
private volatile boolean keepRunning = true;
public Consumer(BlockingQueue<String> queue) {
this.queue = queue;
}
#Override
public void run() {
while (keepRunning) {
String value;
try {
value = queue.take();
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(value);
}
}
}
EDIT The execution is stuck at queue.take() in Consumer Class.
Can anyone please help me fix this problem ? Why is the execution stuck in the consumer ?
One possible solution:
1) On Producer side, put a "END" signal after original 10 puts:
queue.put("END");
2) On Consumer side, once detect "END" signal, break the loop:
public void run() {
while (keepRunning) {
String value;
try {
value = queue.take();
if(value.equals("END")) {
System.out.println("Get END signal. All done!");
break;
}
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(value);
}
}

Multi threaded java program to print even and odd numbers alternatively

I was asked to write a two-threaded Java program in an interview. In this program one thread should print even numbers and the other thread should print odd numbers alternatively.
Sample output:
Thread1: 1
Thread2: 2
Thread1: 3
Thread2: 4
... and so on
I wrote the following program. One class Task which contains two methods to print even and odd numbers respectively. From main method, I created two threads to call these two methods. The interviewer asked me to improve it further, but I could not think of any improvement. Is there any better way to write the same program?
class Task
{
boolean flag;
public Task(boolean flag)
{
this.flag = flag;
}
public void printEven()
{
for( int i = 2; i <= 10; i+=2 )
{
synchronized (this)
{
try
{
while( !flag )
wait();
System.out.println(i);
flag = false;
notify();
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
}
}
public void printOdd()
{
for( int i = 1; i < 10; i+=2 )
{
synchronized (this)
{
try
{
while(flag )
wait();
System.out.println(i);
flag = true;
notify();
}
catch(InterruptedException ex)
{
ex.printStackTrace();
}
}
}
}
}
public class App {
public static void main(String [] args)
{
Task t = new Task(false);
Thread t1 = new Thread( new Runnable() {
public void run()
{
t.printOdd();
}
});
Thread t2 = new Thread( new Runnable() {
public void run()
{
t.printEven();
}
});
t1.start();
t2.start();
}
}
I think this should work properly and pretty simple.
package com.simple;
import java.util.concurrent.Semaphore;
/**
* #author Evgeny Zhuravlev
*/
public class ConcurrentPing
{
public static void main(String[] args) throws InterruptedException
{
Semaphore semaphore1 = new Semaphore(0, true);
Semaphore semaphore2 = new Semaphore(0, true);
new Thread(new Task("1", 1, semaphore1, semaphore2)).start();
new Thread(new Task("2", 2, semaphore2, semaphore1)).start();
semaphore1.release();
}
private static class Task implements Runnable
{
private String name;
private long value;
private Semaphore semaphore1;
private Semaphore semaphore2;
public Task(String name, long value, Semaphore semaphore1, Semaphore semaphore2)
{
this.name = name;
this.value = value;
this.semaphore1 = semaphore1;
this.semaphore2 = semaphore2;
}
#Override
public void run()
{
while (true)
{
try
{
semaphore1.acquire();
System.out.println(name + ": " + value);
value += 2;
semaphore2.release();
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
}
}
}
}
Well, there are many alternatives. I would probably use a SynchronousQueue instead (I don't like low-level wait/notify and try to use higher-level concurrency primitives instead). Also printOdd and printEven could be merged into single method and no additional flags are necessary:
public class App {
static class OddEven implements Runnable {
private final SynchronousQueue<Integer> queue = new SynchronousQueue<>();
public void start() throws InterruptedException {
Thread oddThread = new Thread(this);
Thread evenThread = new Thread(this);
oddThread.start();
queue.put(1);
evenThread.start();
}
#Override
public void run() {
try {
while (true) {
int i = queue.take();
System.out.println(i + " (" + Thread.currentThread() + ")");
if (i == 10)
break;
queue.put(++i);
if (i == 10)
break;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws InterruptedException {
new OddEven().start();
}
}
Is there any better way to write the same program?
Well, the thing is, the only good way to write the program is to use a single thread. If you want a program to do X, Y, and Z in that order, then write a procedure that does X, then Y, then Z. There is no better way than that.
Here's what I would have written after discussing the appropriateness of threads with the interviewer.
import java.util.concurrent.SynchronousQueue;
import java.util.function.Consumer;
public class EvenOdd {
public static void main(String[] args) {
SynchronousQueue<Object> q1 = new SynchronousQueue<>();
SynchronousQueue<Object> q2 = new SynchronousQueue<>();
Consumer<Integer> consumer = (Integer count) -> System.out.println(count);
new Thread(new Counter(q1, q2, 2, 1, consumer)).start();
new Thread(new Counter(q2, q1, 2, 2, consumer)).start();
try {
q1.put(new Object());
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
private static class Counter implements Runnable {
final SynchronousQueue<Object> qin;
final SynchronousQueue<Object> qout;
final int increment;
final Consumer<Integer> consumer;
int count;
Counter(SynchronousQueue<Object> qin, SynchronousQueue<Object> qout,
int increment, int initial_count,
Consumer<Integer> consumer) {
this.qin = qin;
this.qout = qout;
this.increment = increment;
this.count = initial_count;
this.consumer = consumer;
}
public void run() {
try {
while (true) {
Object token = qin.take();
consumer.accept(count);
qout.put(token);
count += increment;
}
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}
}
How about a shorter version like this:
public class OddEven implements Runnable {
private static volatile int n = 1;
public static void main(String [] args) {
new Thread(new OddEven()).start();
new Thread(new OddEven()).start();
}
#Override
public void run() {
synchronized (this.getClass()) {
try {
while (n < 10) {
this.getClass().notify();
this.getClass().wait();
System.out.println(Thread.currentThread().getName() + ": " + (n++));
this.getClass().notify();
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
There is a bit of a trick to kick-start the threads properly - thus the need to an extra notify() to start the whole thing (instead of have both processes wait, or required the main Thread to call a notify) and also to handle the possibility that a thread starts, does it's work and calls notify before the second thread has started :)
My initial answer was non-functional. Edited:
package test;
public final class App {
private static volatile int counter = 1;
private static final Object lock = new Object();
public static void main(String... args) {
for (int t = 0; t < 2; ++t) {
final int oddOrEven = t;
new Thread(new Runnable() {
#Override public void run() {
while (counter < 100) {
synchronized (lock) {
if (counter % 2 == oddOrEven) {
System.out.println(counter++);
}
}
}
}
}).start();
}
}
}

How to make N Threads take turns in cyclic manner in Java?

I recently went through this question on Stackoverflow, where problem is to print even and odd in two threads, in such a manner that they are printed in incremental order. The question is here, where I have provided one solution. This led me to think, what should we do, if we need N Threads to take turn in cyclical manner, in a predefined order ? I tried using CyclicBarrier for this. This is my code :
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class NThreadTurnTaking {
public static void main(String[] args) {
NThreadTurnTaking nThreadTurnTaking = new NThreadTurnTaking();
CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
NThreadTurnTaking.A a = nThreadTurnTaking.new A(cyclicBarrier);
NThreadTurnTaking.B b = nThreadTurnTaking.new B(cyclicBarrier);
NThreadTurnTaking.C c = nThreadTurnTaking.new C(cyclicBarrier);
Thread t1 = new Thread(a);
Thread t2 = new Thread(b);
Thread t3 = new Thread(c);
t1.start();
t2.start();
t3.start();
}
class A implements Runnable{
private final CyclicBarrier cyclicBarrier;
public A(CyclicBarrier cyclicBarrier) {
super();
this.cyclicBarrier = cyclicBarrier;
}
#Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("A");
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
class B implements Runnable{
private final CyclicBarrier cyclicBarrier;
public B(CyclicBarrier cyclicBarrier) {
super();
this.cyclicBarrier = cyclicBarrier;
}
#Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("B");
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
class C implements Runnable{
private final CyclicBarrier cyclicBarrier;
public C(CyclicBarrier cyclicBarrier) {
super();
this.cyclicBarrier = cyclicBarrier;
}
#Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("C");
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
}
I want my program to print A->B->C in order. While using a CyclicBarrier does ensure that they are printed one after another, but the order is not being maintained, which is obvious, because I am not doing anything particular to tell my program that I want a specific order. This is the output :
B
C
A
A
C
B
B
A
C.....
So, how do we ensure order here ? Kindly help.

How to make sure two threads printing even odd numbers maintain even first then odd order for this implementation?

I have created two runnable jobs: PrintEvenNumbersJob and PrintOddNumbersJob and spawned two threads to execute these jobs. This seems to work perfectly fine! But I smell something suspicious about this implementation. Can I have some comments and advice on this implementation?
The problem that I see with this implementation is that the program terminates only when thread1 gains the lock to the object lock first otherwise it print the odd first even second order and doesn't terminate unless I supply yet another statement "lock.notify" after for statement in PrintEvenNumbersJob (as in this implementation). My question here is how to make sure that thread1 is executed first.
public class PrintEvenNumbersJob implements Runnable {
private Object lock;
public PrintEvenNumbersJob(Object lock) {
this.lock = lock;
}
#Override
public void run() {
synchronized (lock) {
for (int i = 0; i <= 10; i += 2) {
lock.notify();
System.out.println(i);
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.notify(); // not required if thread1 gains lock first
}
}
}
public class PrintOddNumbersJob implements Runnable {
private Object lock;
public PrintOddNumbersJob(Object lock) {
this.lock = lock;
}
#Override
public void run() {
synchronized (lock) {
for (int i = 1; i < 10; i += 2) {
lock.notify();
System.out.println(i);
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.notify();
}
}
}
public class EvenOddManager {
public static void main(String[] args) {
Object lock = new Object();
PrintEvenNumbersJob printEvenNumbersJob = new PrintEvenNumbersJob(lock);
PrintOddNumbersJob printOddNumbersJob = new PrintOddNumbersJob(lock);
Thread thread1 = new Thread(printEvenNumbersJob);
Thread thread2 = new Thread(printOddNumbersJob);
thread2.start();
thread1.start();
}
}
Have you try using Semaphores? It's easier because you don't need to worry about the order that wait and notify are called (if you call notify before the wait, it's "lost")
Sample code:
import java.util.concurrent.*;
public class Test {
private final Semaphore oddJobPermits = new Semaphore(0);
private final Semaphore evenJobPermits = new Semaphore(1);
private class EvenJob implements Runnable {
public void run() {
for (int i = 0; i < 10; i++) {
try {
evenJobPermits.acquire();
System.out.println(i * 2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
oddJobPermits.release();
}
}
}
}
private class OddJob implements Runnable {
public void run() {
for (int i = 0; i < 10; i++) {
try {
oddJobPermits.acquire();
System.out.println(i * 2 + 1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
evenJobPermits.release();
}
}
}
}
public void run() {
new Thread(new EvenJob()).start();
new Thread(new OddJob()).start();
}
public static void main(String[] args) {
new Test().run();
}
}
I believe you will need a referee:
public class Referee {
private boolean evensTurn = true;
public void waitMyTurn(boolean even) {
synchronized(this) {
while (even != evensTurn) {
try {
wait();
} finally {
}
}
}
}
public void done() {
synchronized(this) {
evensTurn = !evensTurn;
notify();
}
}
}
public class PrintEvenNumbersJob implements Runnable {
private Referee referee;
public PrintEvenNumbersJob(Referee referee) {
this.referee = referee;
}
#Override
public void run() {
for (int i = 0; i <= 10; i += 2) {
referee.waitMyTurn(true);
System.out.println(i);
referee.done();
}
}
}
public class PrintOddNumbersJob implements Runnable {
private Referee referee;
public PrintOddNumbersJob(Referee referee) {
this.referee = referee;
}
#Override
public void run() {
for (int i = 0; i <= 10; i += 2) {
referee.waitMyTurn(false);
System.out.println(i);
referee.done();
}
}
}
I tried and tested this code. It works using Semaphore
public class TestSemaphore
{
public static void main(String[] args)
throws Exception
{
AtomicInteger count = new AtomicInteger();
Semaphore s = new Semaphore(1, true);
Semaphore t = new Semaphore(1, true);
OddNumberThread oThread = new OddNumberThread(count, s, t);
EvenNumberThread eThread = new EvenNumberThread(count, s, t);
eThread.start();
oThread.start();
}
static class EvenNumberThread
extends Thread
{
private AtomicInteger count;
private Semaphore s, t;
public EvenNumberThread(AtomicInteger pCount, Semaphore pS, Semaphore pT)
{
super("Even");
count = pCount;
s = pS;
t = pT;
}
#Override
public void run()
{
// Make this thread wait until even thread starts, Order will be incorrect if removed these lines.
s.acquireUninterruptibly();
while (count.intValue() <= 10)
{
try
{
// Double checking to make it work
s.acquireUninterruptibly();
System.out.println(getName() + " " + count.getAndIncrement());
}
finally
{
t.release();
}
}
}
}
static class OddNumberThread
extends Thread
{
private AtomicInteger count;
private Semaphore s, t;
public OddNumberThread(AtomicInteger pCount, Semaphore pS, Semaphore pT)
{
super("Odd");
count = pCount;
s = pS;
t = pT;
}
#Override
public void run()
{
// Start this thread first and start printing, Order will be incorrect if removed these lines.
t.acquireUninterruptibly();
s.release();
while (count.intValue() <= 10)
{
try
{
t.acquireUninterruptibly();
System.out.println(getName() + " " + count.getAndIncrement());
}
finally
{
s.release();
}
}
}
}
}

Wait until child threads completed : Java

Problem description : -
Step 1: Take input FILE_NAME from user at main thread.
Step 2: Perform 10 operations on that file (i.e count chars, count lines etc.. ), and all those 10 operations must be in septate threads. It means there must be 10 child threads.
Step 3: Main thread waits until all those child threads completed.
Step 4: Print result.
What I did :-
I did a sample code with 3 threads. I don't want file operation code from your side.
public class ThreadTest {
// This is object to synchronize on.
private static final Object waitObject = ThreadTest.class;
// Your boolean.
private static boolean boolValue = false;
public final Result result = new Result();
public static void main(String[] args) {
final ThreadTest mytest = new ThreadTest();
System.out.println("main started");
new Thread(new Runnable() {
public void run() {
System.out.println("Inside thread");
//Int initialiser
new Thread(new Runnable() {
public void run() {
System.out.println("Setting integer value");
mytest.result.setIntValue(346635);
System.out.println("Integer value seted");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
//String initialiser
new Thread(new Runnable() {
public void run() {
System.out.println("Setting string value");
mytest.result.setStringValue("Hello hi");
System.out.println("String value seted");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
//Boolean initialiser
new Thread(new Runnable() {
public void run() {
System.out.println("Setting boolean value");
mytest.result.setBoolValue(true);
System.out.println("Boolean value seted");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
System.out.println("Thread is finished");
//Notify to main thread
synchronized (ThreadTest.waitObject) {
ThreadTest.boolValue = true;
ThreadTest.waitObject.notifyAll();
}
}
}).start();
try {
synchronized (ThreadTest.waitObject) {
while (!ThreadTest.boolValue) {
ThreadTest.waitObject.wait();
}
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
System.out.println("main finished");
System.out.println("Result is : " + mytest.result.toString());
}
}
Problem :-
My above code is not giving correct answer. How can I do that?
Alternate solutions:
CountDownLatch class does the same. But I don't want to use that class.
I looked this similar solution and I want to use methods of Thread only.
You can do:
Thread t = new Thread() {
public void run() {
System.out.println("text");
// other complex code
}
};
t.start();
t.join();
This way you will wait until the thread finishes and just then continue. You can join multiple threads:
for (Thread thread : threads) {
thread.join();
}
I would recommend looking at the Executors framework first, and then look into the CompletionService.
Then you can write something like this:
ExecutorService executor = Executors.newFixedThreadPool(maxThreadsToUse);
CompletionService completion = new ExecutorCompletionService(executor);
for (each sub task) {
completion.submit(new SomeTaskYouCreate())
}
// wait for all tasks to complete.
for (int i = 0; i < numberOfSubTasks; ++i) {
completion.take(); // will block until the next sub task has completed.
}
executor.shutdown();
In Java 8 a far better approach is to use parallelStream()
Note: it is far easier to see exactly what these background tasks are doing.
public static void main(String[] args) {
Stream.<Runnable>of(
() -> mytest.result.setIntValue(346635),
() -> mytest.result.setStringValue("Hello hi"),
() -> mytest.result.setBoolValue(true) )
.parallel()
.forEach(Runnable::run);
System.out.println("main finished");
System.out.println("Result is : " + mytest.result.toString());
}
I took out the debug information and the sleep as these don't alter the outcome.
You may want to choose CountDownLatch from java.util.concurrent. From JavaDocs:
A synchronization aid that allows one or more threads to wait until a
set of operations being performed in other threads completes.
Sample code:
import java.util.concurrent.CountDownLatch;
public class Test {
private final ChildThread[] children;
private final CountDownLatch latch;
public Test() {
this.children = new ChildThread[4];
this.latch = new CountDownLatch(children.length);
children[0] = new ChildThread(latch, "Task 1");
children[1] = new ChildThread(latch, "Task 2");
children[2] = new ChildThread(latch, "Task 3");
children[3] = new ChildThread(latch, "Task 4");
}
public void run() {
startChildThreads();
waitForChildThreadsToComplete();
}
private void startChildThreads() {
Thread[] threads = new Thread[children.length];
for (int i = 0; i < threads.length; i++) {
ChildThread child = children[i];
threads[i] = new Thread(child);
threads[i].start();
}
}
private void waitForChildThreadsToComplete() {
try {
latch.await();
System.out.println("All child threads have completed.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private class ChildThread implements Runnable {
private final String name;
private final CountDownLatch latch;
protected ChildThread(CountDownLatch latch, String name) {
this.latch = latch;
this.name = name;
}
#Override
public void run() {
try {
// Implementation
System.out.println(name + " has completed.");
} finally {
latch.countDown();
}
}
}
public static void main(String[] args) {
Test test = new Test();
test.run();
}
}
Output:
Task 1 has completed.
Task 4 has completed.
Task 3 has completed.
Task 2 has completed.
All child threads have completed.
There are many ways to approach this. Consider CountDownLatch:
import java.util.concurrent.CountDownLatch;
public class WorkerTest {
final int NUM_JOBS = 3;
final CountDownLatch countDownLatch = new CountDownLatch(NUM_JOBS);
final Object mutex = new Object();
int workData = 0;
public static void main(String[] args) throws Exception {
WorkerTest workerTest = new WorkerTest();
workerTest.go();
workerTest.awaitAndReportData();
}
private void go() {
for (int i = 0; i < NUM_JOBS; i++) {
final int fI = i;
Thread t = new Thread() {
public void run() {
synchronized(mutex) {
workData++;
}
try {
Thread.sleep(fI * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
}
};
t.start();
}
}
private void awaitAndReportData() throws InterruptedException {
countDownLatch.await();
synchronized(mutex) {
System.out.println("All workers done. workData=" + workData);
}
}
}
Check if all child threads are dead, every n seconds. Simple, yet effective method:
boolean allDead=false;
while(! allDead){
allDead=true;
for (int t = 0; t < threadCount; t++)
if(threads[t].isAlive()) allDead=false;
Thread.sleep(2000);
}

Categories

Resources