I was looking at the ThreadPoolExecutor class and I found that it allows to specify the maximum pool size and the core pool size.
I understand, a little, about when to change the core and maximum pool sizes based on the answer here: When is specifying separate core and maximum pool sizes in ThreadPoolExecutor a good idea?
However, I would like to know what are these 'core threads'. I always get 0 when I use the getCorePoolSize() method of a ThreadPoolExecutor
SSCCE here:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
public class PoolSize {
public static void main(String[] args) {
// Create a cached thread pool
ExecutorService cachedPool = Executors.newCachedThreadPool();
// Cast the object to its class type
ThreadPoolExecutor pool = (ThreadPoolExecutor) cachedPool;
// Create a Callable object of anonymous class
Callable<String> aCallable = new Callable<String>(){
String result = "Callable done !";
#Override
public String call() throws Exception {
// Print a value
System.out.println("Callable at work !");
// Sleep for 5 sec
Thread.sleep(0);
return result;
}
};
// Create a Runnable object of anonymous class
Runnable aRunnable = new Runnable(){
#Override
public void run() {
try {
// Print a value
System.out.println("Runnable at work !");
// Sleep for 5 sec
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
// Submit the two tasks for execution
Future<String> callableFuture = cachedPool.submit(aCallable);
Future<?> runnableFuture = cachedPool.submit(aRunnable);
System.out.println("Core threads: " + pool.getCorePoolSize());
System.out.println("Largest number of simultaneous executions: "
+ pool.getLargestPoolSize());
System.out.println("Maximum number of allowed threads: "
+ pool.getMaximumPoolSize());
System.out.println("Current threads in the pool: "
+ pool.getPoolSize());
System.out.println("Currently executing threads: "
+ pool.getTaskCount());
pool.shutdown(); // shut down
}
}
core threads is the minimum which is always running just in case you want to pass it a task. The cached pool by default has a core of 0 as you might expect.
For the fixed thread pool, the core and the maximum are the same i.e. whatever you set the fixed size to.
The core threads are just standard threads but will be always kept alive in the pool, and then the other non-core threads will end their lives after the run() method finished.
But how could these core threads be always alive? That's because they are always waiting for taking a task from the workQueue shared within the pool. By default, the workQueue is a BlockingQueue, its take() method will block the current thread indefinitely until a task becomes available.
Here comes the key point, which threads will become the core threads? They may not be the first started ones or the last ones, but the ones(corePoolSize) that last the longest. Easier to understand from the code.
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
//------------- key code ------------------
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
//------------- key code ------------------
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
What I just said above is based on allowCoreThreadTimeOut set as false.
Actually, I prefer to call core threads as core workers.
Related
In the code below I have a question regarding what happens after I call wait(). In my code, I am returning a value after calling wait(), what does this actually do? I thought that calling wait() suspends the current thread, but what happens to the value i passed to addWorkItem(Integer i) if wait() is called without returning false? You can see in the producer thread that it adds i to a retry buffer if it couldn't be added to the deque. If I don't return false after wait, does the value i just get lost, or is it still there once the thread wakes up?
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
public class ConsumerProducer2 {
private static int QUEUE_SIZE = 10;
private Deque<Integer> queue = new ArrayDeque<Integer>(QUEUE_SIZE);
public synchronized boolean addWorkItem(Integer i) {
while (queue.size() >= QUEUE_SIZE) {
try {
wait();
return false; // WHAT HAPPENS HERE?
} catch (InterruptedException ex) {}
}
queue.addLast(i);
notify();
return true;
}
public synchronized Integer getWork() {
while (queue.size() == 0) {
try {
wait();
return null; // WHAT HAPPENS HERE?
} catch (InterruptedException ex) {
}
}
Integer i = queue.removeFirst();
notify();
return i;
}
public static void main(String[] args) {
new ConsumerProducer2().go();
}
public void go() {
ConsumerThread ct = new ConsumerThread();
ct.start();
ConsumerThread ct2 = new ConsumerThread();
ct2.start();
ProducerThread pt = new ProducerThread();
pt.start();
}
class ConsumerThread extends Thread {
public void run() {
while(true) {
Integer work = getWork();
if (work == null) {
} else {
System.out.println("Thread: " + this.getId() + " received work: " + work);
}
}
}
}
class ProducerThread extends Thread {
private List<Integer> retryList = new ArrayList<Integer>();
public void run() {
while(true) {
Integer currWork;
if (retryList.size() == 0) {
currWork = (int) (Math.random() * 100);
} else {
currWork = retryList.remove(0);
System.out.println("Thread: " + this.getId() + " retrying old work: " + currWork);
}
if (!addWorkItem(currWork)) {
System.out.println("Thread: " + this.getId() + " could not add work (because buffer is probably full): " + currWork);
retryList.add(currWork);
} else {
System.out.println("Thread: " + this.getId() + " added work to queue: " + currWork);
}
}
}
}
}
Having the producer maintain a retry buffer does keep the i value from getting lost, but this still isn't a good way to write the method.
Returning from inside the while loop doesn't make sense. You check the size of the queue, and if it's maxed out you wait around until you get a notification that the size of the queue changed, then inexplicably return false (??). The waiting doesn't really accomplish anything.
The point of waiting in addWorkItem is to delay your thread until the queue has room for the new value. You should wait inside a loop, where when you come out of the wait, your thread reacquires the lock and re-checks the condition (queue size > max) to see if it can add the item yet.
Once the thread has exited from the while loop it is holding the lock, it is sure there's enough room in the queue for the new item (because no other threads can do anything to change the size of the queue while this thread has the lock held), and it can go ahead and add the value to the queue.
You are catching the InterruptedException in an unproductive way, because you catch it, don't bother to restore the interrupt flag, and go back to the top of the while loop. You should be using the interruption to quit waiting and get out of the method. Letting InterruptedException be thrown here would make more sense; the thread running the method should know better how to handle the interruption than this object does.
You shouldn't assume wait returns only when the thread is notified, it can return without a notification. That's one of the reasons to call wait in a loop.
Reworked version:
public synchronized boolean addWorkItem(Integer i) throws InterruptedException {
while (queue.size() >= QUEUE_SIZE) {
wait();
}
queue.addLast(i);
notify();
return true;
}
If you want an excuse to return false from this you could make the method return false if the queue doesn't make room for the new entry within some time frame (having a timeout can be a good thing in a lot of real-life situations):
public synchronized boolean addWorkItem(Integer i) throws InterruptedException {
final long maxWaitTime = 60L * 1000;
long totalWaitTime = 0;
while (queue.size() >= QUEUE_SIZE && totalWaitTime <= maxWaitTime) {
long waitStartTime = System.currentTimeMillis();
wait(maxWaitTime);
totalWaitTime += (System.currentTimeMillis() - waitStartTime);
}
if (queue.size() >= QUEUE_SIZE) {
return false;
}
queue.addLast(i);
notify();
return true;
}
This will still use the retry buffer (which the first version above it won't do at all), but probably not nearly as much as you are now.
Another thing: you have producer and consumer threads concurrently accessing this, and notify is called for both cases. Since notify only wakes up one thread, it's possible for a thread to get a notification that isn't relevant for it (so the notified thread wakes up, checks its condition and finds it still false, then waits some more, while another thread that the notification actually matters to never finds out about it). There are different ways to solve the problem, you can
assign separate locks, one for producers and one for consumers,
reduce the timeout passed into the wait method so you're less dependent on getting notified, or
you can use notifyAll (less performant but a quick fix).
Have a look at this.
Short story: A waiting thread can be woken up by another one calling notify. So in your case addWorkItem will return false in a thread that called wait() just after another thread calls notify().
Also having a look at your logic I think you are trying to block the consumer when the queue is empty and awake it when there is job to be done.
And you want the producer not to deliver new jobs until the queue is empty.
If this is the case, then calling return after waiting will just close your consumer/producer not letting them finish their jobs when they can.
As mentioned in the link below:-
How to get the ThreadPoolExecutor to increase threads to max before queueing?
I changed the queue implementation to return false after entering element.
As a result of which whenever a new task is inserted into the queue a new thread is created for it.
But when i ran the below implementation on a large scale (Bis System Testing) with loggers placed a new problem is generated.
When a task comes for execution it gets inserted into the queue and as queue returns false a new thread is created for its execution. Idle threads which are currently there in the pool are not picked up. Reason being Tasks are assigned to idle threads from getTask() method which picks tasks from queue. So my question is how to change this behavior so that if threads are idle how to make sure that idle threads are assigned tasks for execution rather than creating new threads ??
Below output will make it more clear:-
Task 46 ends
Active Count: 0 Pool Size : 3 Idle Count: 3 Queue Size: 0
Task 47 ends
Active Count: 0 Pool Size : 3 Idle Count: 3 Queue Size: 0
Task 48 ends
Active Count: 0 Pool Size : 3 Idle Count: 3 Queue Size: 0
Active Count: 1 Pool Size : 4 Idle Count: 3 Queue Size: 0
Task 49 ends
Active Count: 2 Pool Size : 5 Idle Count: 3 Queue Size: 0
Task 50 ends
Active Count: 2 Pool Size : 5 Idle Count: 3 Queue Size: 0
The code files are as follows:-
ThreadPoolExecutor is of version java 1.5 as we are using 1.5 on server machine and cannot upgrade it.
ThreadPoolExecutor:-
public void execute(Runnable command) {
System.out.println("Active Count: " + getActiveCount()
+ " Pool Size : " + getPoolSize() + " Idle Count: "
+ (getPoolSize() - getActiveCount())+" Queue Size: "+getQueue().size());
if (command == null)
throw new NullPointerException();
for (;;) {
if (runState != RUNNING) {
reject(command);
return;
}
if (poolSize < corePoolSize && addIfUnderCorePoolSize(command))
return;
if (workQueue.offer(command))
return;
int status = addIfUnderMaximumPoolSize(command);
if (status > 0) // created new thread
return;
if (status == 0) { // failed to create thread
reject(command);
return;
}
// Retry if created a new thread but it is busy with another task
}
}
LinkedBlockingQueue:-
public class CustomBlockingQueue<E> extends LinkedBlockingQueue<E>
{
/**
*
*/
private static final long serialVersionUID = 1L;
public CustomBlockingQueue() {
super(Integer.MAX_VALUE);
}
public boolean offer(E e) {
return false;
}
}
In rejection handler we are calling put method of queue which we haven't overriden
Callingexecutor
final CustomThreadPoolExecutor tpe = new CustomThreadPoolExecutor(3, 8, 0L, TimeUnit.MILLISECONDS, new MediationBlockingQueue<Runnable>(), new MediationRejectionHandler());
private static final int TASK_COUNT = 100;
for (int i = 0; i < TASK_COUNT; i++) {
......
tpe.execute(new Task(i));
.....
}
We are calling the executor with core pool size as 3 max pool size as 8 and using unbounded linked blocking queue for tasks.
The easiest way to achieve the “start before queuing but prefer existing threads” behavior using a SynchronousQueue. It will accept offered items if and only if there’s already a waiting receiver. So idle threads will get items and once there are no idle threads the ThreadPoolExecutor will start new threads.
The only disadvantage is that once all threads are started, you can’t simply put the pending item into the queue as it has no capacity. So you either have to accept that the submitter gets blocked or you need another queue for putting pending tasks to it and another background thread which tries to put these pending items to the synchronous queue. This additional thread won’t hurt the performance as it is blocked in either of these two queues most of the time.
class QueuingRejectionHandler implements RejectedExecutionHandler {
final ExecutorService processPending=Executors.newSingleThreadExecutor();
public void rejectedExecution(
final Runnable r, final ThreadPoolExecutor executor) {
processPending.execute(new Runnable() {
public void run() {
executor.execute(r);
}
});
}
}
…
ThreadPoolExecutor e=new ThreadPoolExecutor(
corePoolSize, maximumPoolSize, keepAliveTime, unit,
new SynchronousQueue<Runnable>(), new QueuingRejectionHandler());
I believe that you problem is in the following:
public boolean offer(E e) {
return false;
}
This will always return false to the TPE which will cause it to start another thread, regardless of how many threads are currently idle. This is not what my code sample on this answer recommends. I had to correct an early problem with it after feedback.
My answer says to make your offer(...) method look something like:
public boolean offer(Runnable e) {
/*
* Offer it to the queue if there is 1 or 0 items already queued, else
* return false so the TPE will add another thread.
*/
if (size() <= 1) {
return super.offer(e);
} else {
return false;
}
}
So if there are 2 or more things already in the queue, it will fork another thread otherwise it will enqueue the task in queue which should be picked up by the idle threads. You might also play with the 1 value. Trying it with 0 or more than 1 may be appropriate for your application. Injecting that value into your CustomBlockingQueue might be in order.
Solution given by Gray here is awesome, but I faced same problem as yours i.e ideal threads were not used to pick new task coming, but new thread was created in case poolSize is less than maxPoolSize.
So, I tried to tweak functionality of ThreadPoolExecutor itself, by copying complete class(not a good idea, but couldn't find any other solution) and extending it with ThreadPoolExecutor and overriding execute method.
Below is the method :
public void execute(Runnable command)
{
System.out.println("ActiveCount : " + this.getActiveCount()
+ " PoolSize : " + this.getPoolSize() + " QueueSize : "
+ this.getQueue().size());
if (command == null)
throw new NullPointerException();
for (;;)
{
if (runState != RUNNING)
{
reject(command);
return;
}
if (poolSize < corePoolSize && addIfUnderCorePoolSize(command))
return;
//Now, it will never offer to queue but will go further for thread creation.
//if (workQueue.offer(command))
//return;
//This check is introduced to utilized ideal threads instead of creating new thread
//for incoming tasks.
//Example : coreSize = 3, maxPoolSize = 8.
//activeCount = 4, and PoolSize = 5, so 1 thread is ideal Currently queue is empty.
//When new task comes, it will offer that to queue, and getTask() will take care and execute the task.
//But if new task comes, before ideal thread takes task from queue,
//activeCount = 4, and PoolSize = 5, so 1 thread is ideal Currently queue size = 1.
//this check fails and new thread is created if poolsize under max size or
//task is added to queue through rejection handler.
if ((this.getPoolSize() - this.getActiveCount()) > 0 &&
(this.getPoolSize() - this.getActiveCount() - workQueue.size()) > 0)
{
workQueue.offer(command);
return;
}
int status = addIfUnderMaximumPoolSize(command);
if (status > 0) // created new thread
return;
if (status == 0)
{ // failed to create thread
reject(command);
return;
}
// Retry if created a new thread but it is busy with another task
}
}
In rejection handler I am using put method to put task in queue(unbounded), as suggested by Gray. :)
Note : I am not overriding behavior of Queue in my code.
So my question is how to change this behavior so that if threads are idle how to make sure that idle threads are assigned tasks for execution rather than creating new threads ??
Things have been improved a lot in last couple of years. Your problem has a simple solution with Java 8 Executors newWorkStealingPool API
newWorkStealingPool
public static ExecutorService newWorkStealingPool()
Creates a work-stealing thread pool using all available processors as its target parallelism level.
ExecutorService executorService = Executors.newWorkStealingPool();
will do required magic for you. newWorkSteatingPool will return a ExecutorService of ForkJoinPool type. In ForkJoinPool, Idle threads will steal task from busy thread's queue, which you are looking for.
In this simple short program, you will notice that the program hangs forever because the take() does not release the thread. According to my understanding, take() causes the thread to be released even though the task itself is blocked on take().
Edited:
This works (thanks to you all for fixing the autoboxing):
import java.util.ArrayList;
import java.util.Collection;
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 static int THREAD_COUNT = 5;
public static void main(String[] args) throws ExecutionException, InterruptedException {
final ExecutorService executorPool = Executors.newFixedThreadPool(THREAD_COUNT);
final LinkedBlockingQueue<Long> queue = new LinkedBlockingQueue<Long>();
Collection<Future<Long>> collection = new ArrayList<Future<Long>>();
// producer:
for (int i = 0; i < 20; i++) {
collection.add(executorPool.submit(new Callable<Long>() {
#Override
public Long call() throws Exception {
for (int i = 100; i >= 0; i--) {
queue.put((long) i);
}
return -1L;
}
}));
}
// consumer:
for (int i = 0; i < 20; i++) {
collection.add(executorPool.submit(new Callable<Long>() {
#Override
public Long call() throws Exception {
while (true) {
Long item = queue.take();
if (item.intValue() == 0) {
break;
}
}
return 1L;
}
}));
}
long sum = 0;
for (Future<Long> item : collection) {
sum += item.get();
}
executorPool.shutdown();
System.out.println("sum = " + sum);
}
}
But if you swap the producer and consumer invocations, it will hang:
import java.util.ArrayList;
import java.util.Collection;
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 static int THREAD_COUNT = 5;
public static void main(String[] args) throws ExecutionException, InterruptedException {
final ExecutorService executorPool = Executors.newFixedThreadPool(THREAD_COUNT);
final LinkedBlockingQueue<Long> queue = new LinkedBlockingQueue<Long>();
Collection<Future<Long>> collection = new ArrayList<Future<Long>>();
// consumer:
for (int i = 0; i < 20; i++) {
collection.add(executorPool.submit(new Callable<Long>() {
#Override
public Long call() throws Exception {
while (true) {
Long item = queue.take();
if (item.intValue() == 0) {
break;
}
}
return 1L;
}
}));
}
// producer:
for (int i = 0; i < 20; i++) {
collection.add(executorPool.submit(new Callable<Long>() {
#Override
public Long call() throws Exception {
for (int i = 100; i >= 0; i--) {
queue.put((long) i);
}
return -1L;
}
}));
}
long sum = 0;
for (Future<Long> item : collection) {
sum += item.get();
}
executorPool.shutdown();
System.out.println("sum = " + sum);
}
}
To my understanding the producer and consumer order should not matter. In other words, there is a notion of task and thread. Thread are independent of code program whereas task is associated with a certain program. Therefore, in my example, when the JVM assigns a thread to execute of the Callable tasks, if the consumer is instantiated first, then the task will block on take(). Once the JVM discovers that the task is blocked, it will release the thread (or as I understand it but it is not releasing it) and places it back to the worker thread pool in preparation for processing a runnable task (which in this case are the Producers). Consequently, at the end of instantiating all the Callable's, there should be 40 tasks but only 5 threads; 20 of those tasks are blocked, 5 of the tasks should be running and 15 should be waiting (to run).
I think you misunderstand how threads and threadpools work. A threadpool typically has a work item queue which contains items to be worked on (in your case Callable<>s).
It also contains a (maximum) number of threads (in your case 5) which can work on those items.
The lifetime of an active thread is defined by the code it executes - usually a method. The thread becomes "alive" when it starts executing the method and it ends when it returns. If the method blocks to wait on some signal it does not mean the the thread can go away and execute some other method - that's not how threads work. Instead the thread will be blocked until it can continue execution and enable other threads to be run.
The method which is run by a threadpool thread usually looks like this:
void threadloop()
{
while (!quit)
{
Callable<T> item = null;
synchronized (workQueue)
{
if (workQueue.Count == 0)
workQueue.wait();
// we could have been woken up for some other reason so check again
if (workQueue.Count > 0)
item = workQueue.pop();
}
if (item != null)
item.Call();
}
}
This is more or less pseudo code (I'm not a Java developer) but it should show the concept. Now item.Call() executes the method which is supplied by the user of the pool. If that method blocks, then what happens? Well - the thread will be blocked in its execution of item.Call() until the method wakes up again. It can't just go away and execute some other code arbitrarily.
From javadoc:
Retrieves and removes the head of this queue, waiting if no elements are present on this queue.
It will wait: you're running in main, so it will stay there.
EDIT: correction: the blocking still happens (in the thread pool threads, not in main). There is no yielding going on: the 20 threads are blocked on the take calls, so no put calls execute, so the Futures never complete, so the program hangs.
I don't know what exactly you mean by release thread but once you block on take() the calling thread is blocked and is not going back to the pool.
I think you've misunderstood what gets "blocked" in a BlockingQueue.
The call to queue.take() blocks the thread that invoked it until something is available in the queue. This means that the thread will wait there endlessly, unless interrupted, until an item is added to the queue.
The second code sample hangs the problem because you are adding 20 tasks to wait for an item to appear in the BlockingQueue, and the executor has just 5 threads in it - thus the first five tasks cause all five of the threads to block. This executor is filled with 15 further consumer tasks.
The addition of tasks in the second for-loop to add items to the queue results in 20 tasks that can never be executed, because all threads in the executor are stuck waiting.
So when you say this:
According to my understanding, take() causes the thread to be released even though the task itself is blocked on take().
You have a misunderstanding because there is no difference here between what the "thread" does and what the "task" does. A thread cannot be "released" while the task is blocked - it is the thread that runs the task. When the thread encounters a blocking call to take(), the thread is blocked, period.
I have a java application where the main-thread starts 2 other threads.
If one of these threads terminates, the main-thread may start another thread depending on the result of the terminated thread.
Example:
The main-thread creates 2 threads: A and B. Thread A will load a picture and thread B will load another picture. If A terminates and loaded the picture successfully a new Thread C will be created which does some other stuff and so on.
How can i do this? I do not want to use busy waiting in the main thread and check every 100ms if one of the two threads has finished.
I think i cannot use a thread pool because the number of active threads (in this case A and B) will vary extremely and it's the main-threads dicision to create a new thread or not.
This is rough sketch of the "busy waiting" solution:
public class TestThreads {
private class MyThread extends Thread {
volatile boolean done = false;
int steps;
#Override
public void run() {
for (int i=0; i<steps; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException exc) { }
}
done = true;
synchronized (this) {
notify();
}
}
public void waitFor(long ms) {
synchronized (this) {
try {
wait(ms);
} catch (InterruptedException exc) { }
}
}
}
public void startTest() {
MyThread a = new MyThread();
a.steps = 6;
a.start();
MyThread b = new MyThread();
b.steps = 3;
b.start();
while (true) {
if (!a.done) {
a.waitFor(100);
if (a.done) {
System.out.println("C will be started, because A is done.");
}
}
if (!b.done) {
b.waitFor(100);
if (b.done) {
System.out.println("C will be started, because B is done.");
}
}
if (a.done && b.done) {
break;
}
}
}
public static void main(String[] args) {
TestThreads test = new TestThreads();
test.startTest();
}
}
This sounds like a classic case for using a ThreadPoolExecutor for performing the tasks concurrently, and wrapping it with an ExecutorCompletionService, for collecting the results as they arrive.
For example, assuming that tasks contains a set of tasks to execute in parallel, each returning a String value when it terminates, the code to process the results as they become available can be something like:
List<Callable<String>> tasks = ....;
Executor ex = Executors.newFixedThreadPool(10);
ExecutorCompletionService<String> ecs = new ExecutorCompletionService<String>(ex);
for (Callable<String> task : tasks)
ecs.submit(task);
for(int i = 0; i < tasks.size(); i++) {
String result = ecs.take().get();
//Do something with result
}
If you include the identity of the task as a part of the returned value, then you can make decisions depending on the completion order.
Check Semaphore
A counting semaphore. Conceptually, a semaphore maintains a set of permits. Each acquire() blocks if necessary until a permit is available, and then takes it
So, whenever you thread finishes, it frees one permit, which is then acquired by the main thread
You should use a thread pool. In a thread pool, you have a fixed number of threads and tasks are kept in a queue; whenever a thread is available, a task is taken off the queue and executed by that thread.
Here is a link to the Sun tutorial on thread pooling.
Edit: just noticed that you wrote in your answer that you think you cannot use thread pooling. I don't see why this is the case. You can set threads to be created on-demand rather than all at once if you are worried about creation overhead, and once created an idle thread is not really going to hurt anything.
You also say that it's the main thread's decision to create a new Thread or not, but does it really need to be? I think that may just overcomplicate things for you.
Is there a reason to control the thread execution directly instead of using something like
ExecutorService?
#danben got there first, but I fell into the same pooling trap.
A lot of the complexity in your code is that the main thread is trying to wait on two different objects. There's nothing which says you can't use wait and notify on another object, and if your tasks are ( A or B ) then C, the code below will work - wait on a reference which is set to indicate the first task to complete.
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class BiggieThreads
{
private static class MyTask implements Runnable
{
final int steps;
final AtomicReference<MyTask> shared;
final String name;
MyTask ( int steps, AtomicReference<MyTask> shared, String name )
{
this.shared = shared;
this.steps = steps;
this.name = name;
}
#Override
public void run()
{
for ( int i = 1; i <= steps; i++ ) {
System.out.println ( "Running: " + this + " " + i + "/" + steps);
try {
Thread.sleep ( 100 );
} catch ( InterruptedException exc ) { }
}
// notify if this is the first to complete
if ( shared.compareAndSet ( null, this ) )
synchronized ( shared ) {
shared.notify();
}
System.out.println ( "Completed: " + this );
}
#Override
public String toString ()
{
return name;
}
}
public void startTest() throws InterruptedException
{
final ExecutorService pool = Executors.newFixedThreadPool ( 3 );
final AtomicReference<MyTask> shared = new AtomicReference<MyTask>();
Random random = new Random();
synchronized ( shared ) {
// tasks launched while lock on shared held to prevent
// them notifying before this thread waits
pool.execute ( new MyTask ( random.nextInt ( 5 ) + 3, shared, "a" ) );
pool.execute ( new MyTask ( random.nextInt ( 5 ) + 3, shared, "b" ) );
shared.wait();
}
System.out.println ( "Reported: " + shared.get() );
pool.shutdown();
}
public static void main ( String[] args ) throws InterruptedException
{
BiggieThreads test = new BiggieThreads ();
test.startTest();
}
}
I'd tend to use a semaphore for this job in production, as although the wait is quite simple, using in semaphore puts a name to the behaviour, so there's less to work out when you next read the code.
Let us suppose that I have a thread that consumes items produced by another thread. Its run method is as follows, with inQueue being a BlockingQueue
boolean shutdown = false;
while (!shutdown) {
try {
WorkItem w = inQueue.take();
w.consume();
} catch (InterruptedException e) {
shutdown = true;
}
}
Furthermore, a different thread will signal that there are no more work items by interrupting this running thread. Will take() throw an interrupted exception if it does not need to block to retrieve the next work item. i.e. if the producer signals that it is done filling the work queue, is it possible to accidentally leave some items in inQueue or miss the interrupt?
A good way to signal termination of a blocking queue is to submit a 'poison' value into the queue that indicates a shutdown has occurred. This ensures that the expected behavior of the queue is honored. Calling Thread.interupt() is probably not a good idea if you care about clearing the queue.
To provide some code:
boolean shutdown = false;
while (!shutdown) {
try {
WorkItem w = inQueue.take();
if (w == QUEUE_IS_DEAD)
shutdown = true;
else
w.consume();
} catch (InterruptedException e) {
// possibly submit QUEUE_IS_DEAD to the queue
}
}
I wondered about the same thing and reading the javadoc for take() I believed that it would throw an interrupted exception only after having taken all the items in the queue, since if the queue had items, it would not have to "wait".
But I made a small test:
package se.fkykko.slask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicLong;
public class BlockingQueueTakeTest {
public static void main(String[] args) throws Exception {
Runner t = new Runner();
Thread t1 = new Thread(t);
for (int i = 0; i < 50; i++) {
t.queue.add(i);
}
System.out.println(("Number of items in queue: " + t.queue.size()));
t1.start();
Thread.sleep(1000);
t1.interrupt();
t1.join();
System.out.println(("Number of items in queue: " + t.queue.size()));
System.out.println(("Joined t1. Finished"));
}
private static final class Runner implements Runnable {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(100);
AtomicLong m_count = new AtomicLong(0);
#Override
public void run() {
try {
while (true) {
queue.take();
System.out.println("Took item " + m_count.incrementAndGet());
final long start = System.currentTimeMillis();
while ((System.currentTimeMillis() - start) < 100) {
Thread.yield(); //Spin wait
}
}
}
catch (InterruptedException ex) {
System.out.println("Interrupted. Count: " + m_count.get());
}
}
}
}
The runner will take 10-11 items and then finish i.e. take() will throw InterruptedException even if there still is items in the queue.
Summary: Use the Poison pill approach instead, then you have full control over how much is left in the queue.
According to javadoc, the take() method will throw InterruptedException if interrupted while waiting.
You can't in general interrupt the threads of an ExecutorService from external code if you used ExecutorService::execute(Runnable) to start the threads, because external code does not have a reference to the Thread objects of each of the running threads (see the end of this answer for a solution though, if you need ExecutorService::execute). However, if you instead use ExecutorService::submit(Callable<T>) to submit the jobs, you get back a Future<T>, which internally keeps a reference to the running thread once Callable::call() begins execution. This thread can be interrupted by calling Future::cancel(true). Any code within (or called by) the Callable that checks the current thread's interrupt status can therefore be interrupted via the Future reference. This includes BlockingQueue::take(), which, even when blocked, will respond to thread interruption. (JRE blocking methods will typically wake up if interrupted while blocked, realize they have been interrupted, and throw an InterruptedException.)
To summarize: Future::cancel() and Future::cancel(true) both cancel future work, while Future::cancel(true) also interrupts ongoing work (as long as the ongoing work responds to thread interrupt). Neither of the two cancel invocations affects work that has already successfully completed.
Note that once a thread is interrupted by cancellation, an InterruptException will be thrown within the thread (e.g. by BlockingQueue::take() in this case). However, you a CancellationException will be thrown back in the main thread the next time you call Future::get() on a successfully cancelled Future (i.e. a Future that was cancelled before it completed). This is different from what you would normally expect: if a non-cancelled Callable throws InterruptedException, the next call to Future::get() will throw InterruptedException, but if a cancelled Callable throws InterruptedException, the next call to Future::get() will through CancellationException.
Here's an example that illustrates this:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
public class Test {
public static void main(String[] args) throws Exception {
// Start Executor with 4 threads
int numThreads = 4;
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(numThreads);
try {
// Set up BlockingQueue for inputs, and List<Future> for outputs
BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
List<Future<String>> futures = new ArrayList<>(numThreads);
for (int i = 0; i < numThreads; i++) {
int threadIdx = i;
futures.add(executor.submit(new Callable<String>() {
#Override
public String call() throws Exception {
try {
// Get an input from the queue (blocking)
int val = queue.take();
return "Thread " + threadIdx + " got value " + val;
} catch (InterruptedException e) {
// Thrown once Future::cancel(true) is called
System.out.println("Thread " + threadIdx + " got interrupted");
// This value is returned to the Future, but can never
// be read, since the caller will get a CancellationException
return "Thread " + threadIdx + " got no value";
}
}
}));
}
// Enqueue (numThreads - 1) values into the queue, so that one thread blocks
for (int i = 0; i < numThreads - 1; i++) {
queue.add(100 + i);
}
// Cancel all futures
for (int i = 0; i < futures.size(); i++) {
Future<String> future = futures.get(i);
// Cancel the Future -- this doesn't throw an exception until
// the get() method is called
future.cancel(/* mayInterruptIfRunning = */ true);
try {
System.out.println(future.get());
} catch (CancellationException e) {
System.out.println("Future " + i + " was cancelled");
}
}
} finally {
// Terminate main after all threads have shut down (this call does not block,
// so main will exit before the threads stop running)
executor.shutdown();
}
}
}
Each time you run this, the output will be different, but here's one run:
Future 1 was cancelled
Future 0 was cancelled
Thread 2 got value 100
Thread 3 got value 101
Thread 1 got interrupted
This shows that Thread 2 and Thread 3 completed before Future::cancel() was called. Thread 1 was cancelled, so internally InterruptedException was thrown, and externally CancellationException was thrown. Thread 0 was cancelled before it started running. (Note that the thread indices won't in general correlate with the Future indices, so Future 0 was cancelled could correspond to either thread 0 or thread 1 being cancelled, and the same for Future 1 was cancelled.)
Advanced: one way to achieve the same effect with Executor::execute (which does not return a Future reference) rather than Executor::submit would be to create a ThreadPoolExecutor with a custom ThreadFactory, and have your ThreadFactory record a reference in a concurrent collection (e.g. a concurrent queue) for every thread created. Then to cancel all threads, you can simply call Thread::interrupt() on all previously-created threads. However, you will need to deal with the race condition that new threads may be created while you are interrupting existing threads. To handle this, set an AtomicBoolean flag, visible to the ThreadFactory, that tells it not to create any more threads, then once that is set, cancel the existing threads.
The java.concurrency.utils package was designed and implemented by some of the finest minds in concurrent programming. Also, interrupting threads as a means to terminate them is explicitly endorsed by their book "Java Concurrency in Practice". Therefore, I would be extremely surprised if any items were left in the queue due to an interrupt.