I need something which is directly equivalent to CountDownLatch, but is resettable (remaining thread-safe!). I can't use classic synchronisation constructs as they simply don't work in this situation (complex locking issues). At the moment, I'm creating many CountDownLatch objects, each replacing the previous one. I believe this is doing in the young generation in the GC (due to the sheer number of objects). You can see the code which uses the latches below (it's part of the java.net mock for a ns-3 network simulator interface).
Some ideas might be to try CyclicBarrier (JDK5+) or Phaser (JDK7)
I can test code and get back to anyone that finds a solution to this problem, since I'm the only one who can insert it into the running system to see what happens :)
/**
*
*/
package kokunet;
import java.io.IOException;
import java.nio.channels.ClosedSelectorException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import kokuks.IConnectionSocket;
import kokuks.KKSAddress;
import kokuks.KKSSocket;
import kokuks.KKSSocketListener;
/**
* KSelector
* #version 1.0
* #author Chris Dennett
*/
public class KSelector extends SelectorImpl {
// True if this Selector has been closed
private volatile boolean closed = false;
// Lock for close and cleanup
final class CloseLock {}
private final Object closeLock = new CloseLock();
private volatile boolean selecting = false;
private volatile boolean wakeup = false;
class SocketListener implements KKSSocketListener {
protected volatile CountDownLatch latch = null;
/**
*
*/
public SocketListener() {
newLatch();
}
protected synchronized CountDownLatch newLatch() {
return this.latch = new CountDownLatch(1);
}
protected synchronized void refreshReady(KKSSocket socket) {
if (!selecting) return;
synchronized (socketToChannel) {
SelChImpl ch = socketToChannel.get(socket);
if (ch == null) {
System.out.println("ks sendCB: channel not found for socket: " + socket);
return;
}
synchronized (channelToKey) {
SelectionKeyImpl sk = channelToKey.get(ch);
if (sk != null) {
if (handleSelect(sk)) {
latch.countDown();
}
}
}
}
}
#Override
public void connectionSucceeded(KKSSocket socket) {
refreshReady(socket);
}
#Override
public void connectionFailed(KKSSocket socket) {
refreshReady(socket);
}
#Override
public void dataSent(KKSSocket socket, long bytesSent) {
refreshReady(socket);
}
#Override
public void sendCB(KKSSocket socket, long bytesAvailable) {
refreshReady(socket);
}
#Override
public void onRecv(KKSSocket socket) {
refreshReady(socket);
}
#Override
public void newConnectionCreated(KKSSocket socket, KKSSocket newSocket, KKSAddress remoteaddress) {
refreshReady(socket);
}
#Override
public void normalClose(KKSSocket socket) {
wakeup();
}
#Override
public void errorClose(KKSSocket socket) {
wakeup();
}
}
protected final Map<KKSSocket, SelChImpl> socketToChannel = new HashMap<KKSSocket, SelChImpl>();
protected final Map<SelChImpl, SelectionKeyImpl> channelToKey = new HashMap<SelChImpl, SelectionKeyImpl>();
protected final SocketListener currListener = new SocketListener();
protected Thread selectingThread = null;
SelChImpl getChannelForSocket(KKSSocket s) {
synchronized (socketToChannel) {
return socketToChannel.get(s);
}
}
SelectionKeyImpl getSelKeyForChannel(KKSSocket s) {
synchronized (channelToKey) {
return channelToKey.get(s);
}
}
protected boolean markRead(SelectionKeyImpl impl) {
synchronized (impl) {
if (!impl.isValid()) return false;
impl.nioReadyOps(impl.readyOps() | SelectionKeyImpl.OP_READ);
return selectedKeys.add(impl);
}
}
protected boolean markWrite(SelectionKeyImpl impl) {
synchronized (impl) {
if (!impl.isValid()) return false;
impl.nioReadyOps(impl.readyOps() | SelectionKeyImpl.OP_WRITE);
return selectedKeys.add(impl);
}
}
protected boolean markAccept(SelectionKeyImpl impl) {
synchronized (impl) {
if (!impl.isValid()) return false;
impl.nioReadyOps(impl.readyOps() | SelectionKeyImpl.OP_ACCEPT);
return selectedKeys.add(impl);
}
}
protected boolean markConnect(SelectionKeyImpl impl) {
synchronized (impl) {
if (!impl.isValid()) return false;
impl.nioReadyOps(impl.readyOps() | SelectionKeyImpl.OP_CONNECT);
return selectedKeys.add(impl);
}
}
/**
* #param provider
*/
protected KSelector(SelectorProvider provider) {
super(provider);
}
/* (non-Javadoc)
* #see kokunet.SelectorImpl#implClose()
*/
#Override
protected void implClose() throws IOException {
provider().getApp().printMessage("implClose: closed: " + closed);
synchronized (closeLock) {
if (closed) return;
closed = true;
for (SelectionKey sk : keys) {
provider().getApp().printMessage("dereg1");
deregister((AbstractSelectionKey)sk);
provider().getApp().printMessage("dereg2");
SelectableChannel selch = sk.channel();
if (!selch.isOpen() && !selch.isRegistered())
((SelChImpl)selch).kill();
}
implCloseInterrupt();
}
}
protected void implCloseInterrupt() {
wakeup();
}
private boolean handleSelect(SelectionKey k) {
synchronized (k) {
boolean notify = false;
if (!k.isValid()) {
k.cancel();
((SelectionKeyImpl)k).channel.socket().removeListener(currListener);
return false;
}
SelectionKeyImpl ski = (SelectionKeyImpl)k;
if ((ski.interestOps() & SelectionKeyImpl.OP_READ) != 0) {
if (ski.channel.socket().getRxAvailable() > 0) {
notify |= markRead(ski);
}
}
if ((ski.interestOps() & SelectionKeyImpl.OP_WRITE) != 0) {
if (ski.channel.socket().getTxAvailable() > 0) {
notify |= markWrite(ski);
}
}
if ((ski.interestOps() & SelectionKeyImpl.OP_CONNECT) != 0) {
if (!ski.channel.socket().isConnectionless()) {
IConnectionSocket cs = (IConnectionSocket)ski.channel.socket();
if (!ski.channel.socket().isAccepting() && !cs.isConnecting() && !cs.isConnected()) {
notify |= markConnect(ski);
}
}
}
if ((ski.interestOps() & SelectionKeyImpl.OP_ACCEPT) != 0) {
//provider().getApp().printMessage("accept check: ski: " + ski + ", connectionless: " + ski.channel.socket().isConnectionless() + ", listening: " + ski.channel.socket().isListening() + ", hasPendingConn: " + (ski.channel.socket().isConnectionless() ? "nope!" : ((IConnectionSocket)ski.channel.socket()).hasPendingConnections()));
if (!ski.channel.socket().isConnectionless() && ski.channel.socket().isListening()) {
IConnectionSocket cs = (IConnectionSocket)ski.channel.socket();
if (cs.hasPendingConnections()) {
notify |= markAccept(ski);
}
}
}
return notify;
}
}
private boolean handleSelect() {
boolean notify = false;
// get initial status
for (SelectionKey k : keys) {
notify |= handleSelect(k);
}
return notify;
}
/* (non-Javadoc)
* #see kokunet.SelectorImpl#doSelect(long)
*/
#Override
protected int doSelect(long timeout) throws IOException {
processDeregisterQueue();
long timestartedms = System.currentTimeMillis();
synchronized (selectedKeys) {
synchronized (currListener) {
wakeup = false;
selectingThread = Thread.currentThread();
selecting = true;
}
try {
handleSelect();
if (!selectedKeys.isEmpty() || timeout == 0) {
return selectedKeys.size();
}
//TODO: useless op if we have keys available
for (SelectionKey key : keys) {
((SelectionKeyImpl)key).channel.socket().addListener(currListener);
}
try {
while (!wakeup && isOpen() && selectedKeys.isEmpty()) {
CountDownLatch latch = null;
synchronized (currListener) {
if (wakeup || !isOpen() || !selectedKeys.isEmpty()) {
break;
}
latch = currListener.newLatch();
}
try {
if (timeout > 0) {
long currtimems = System.currentTimeMillis();
long remainingMS = (timestartedms + timeout) - currtimems;
if (remainingMS > 0) {
latch.await(remainingMS, TimeUnit.MILLISECONDS);
} else {
break;
}
} else {
latch.await();
}
} catch (InterruptedException e) {
}
}
return selectedKeys.size();
} finally {
for (SelectionKey key : keys) {
((SelectionKeyImpl)key).channel.socket().removeListener(currListener);
}
}
} finally {
synchronized (currListener) {
selecting = false;
selectingThread = null;
wakeup = false;
}
}
}
}
/* (non-Javadoc)
* #see kokunet.SelectorImpl#implRegister(kokunet.SelectionKeyImpl)
*/
#Override
protected void implRegister(SelectionKeyImpl ski) {
synchronized (closeLock) {
if (closed) throw new ClosedSelectorException();
synchronized (channelToKey) {
synchronized (socketToChannel) {
keys.add(ski);
socketToChannel.put(ski.channel.socket(), ski.channel);
channelToKey.put(ski.channel, ski);
}
}
}
}
/* (non-Javadoc)
* #see kokunet.SelectorImpl#implDereg(kokunet.SelectionKeyImpl)
*/
#Override
protected void implDereg(SelectionKeyImpl ski) throws IOException {
synchronized (channelToKey) {
synchronized (socketToChannel) {
keys.remove(ski);
socketToChannel.remove(ski.channel.socket());
channelToKey.remove(ski.channel);
SelectableChannel selch = ski.channel();
if (!selch.isOpen() && !selch.isRegistered())
((SelChImpl)selch).kill();
}
}
}
/* (non-Javadoc)
* #see kokunet.SelectorImpl#wakeup()
*/
#Override
public Selector wakeup() {
synchronized (currListener) {
if (selecting) {
wakeup = true;
selecting = false;
selectingThread.interrupt();
selectingThread = null;
}
}
return this;
}
}
Cheers,
Chris
I copied CountDownLatch and implemented a reset() method that resets the internal Sync class to its initial state (starting count) :) Appears to work fine. No more unnecessary object creation \o/ It was not possible to subclass because sync was private. Boo.
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
/**
* A synchronization aid that allows one or more threads to wait until
* a set of operations being performed in other threads completes.
*
* <p>A {#code CountDownLatch} is initialized with a given <em>count</em>.
* The {#link #await await} methods block until the current count reaches
* zero due to invocations of the {#link #countDown} method, after which
* all waiting threads are released and any subsequent invocations of
* {#link #await await} return immediately. This is a one-shot phenomenon
* -- the count cannot be reset. If you need a version that resets the
* count, consider using a {#link CyclicBarrier}.
*
* <p>A {#code CountDownLatch} is a versatile synchronization tool
* and can be used for a number of purposes. A
* {#code CountDownLatch} initialized with a count of one serves as a
* simple on/off latch, or gate: all threads invoking {#link #await await}
* wait at the gate until it is opened by a thread invoking {#link
* #countDown}. A {#code CountDownLatch} initialized to <em>N</em>
* can be used to make one thread wait until <em>N</em> threads have
* completed some action, or some action has been completed N times.
*
* <p>A useful property of a {#code CountDownLatch} is that it
* doesn't require that threads calling {#code countDown} wait for
* the count to reach zero before proceeding, it simply prevents any
* thread from proceeding past an {#link #await await} until all
* threads could pass.
*
* <p><b>Sample usage:</b> Here is a pair of classes in which a group
* of worker threads use two countdown latches:
* <ul>
* <li>The first is a start signal that prevents any worker from proceeding
* until the driver is ready for them to proceed;
* <li>The second is a completion signal that allows the driver to wait
* until all workers have completed.
* </ul>
*
* <pre>
* class Driver { // ...
* void main() throws InterruptedException {
* CountDownLatch startSignal = new CountDownLatch(1);
* CountDownLatch doneSignal = new CountDownLatch(N);
*
* for (int i = 0; i < N; ++i) // create and start threads
* new Thread(new Worker(startSignal, doneSignal)).start();
*
* doSomethingElse(); // don't let run yet
* startSignal.countDown(); // let all threads proceed
* doSomethingElse();
* doneSignal.await(); // wait for all to finish
* }
* }
*
* class Worker implements Runnable {
* private final CountDownLatch startSignal;
* private final CountDownLatch doneSignal;
* Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
* this.startSignal = startSignal;
* this.doneSignal = doneSignal;
* }
* public void run() {
* try {
* startSignal.await();
* doWork();
* doneSignal.countDown();
* } catch (InterruptedException ex) {} // return;
* }
*
* void doWork() { ... }
* }
*
* </pre>
*
* <p>Another typical usage would be to divide a problem into N parts,
* describe each part with a Runnable that executes that portion and
* counts down on the latch, and queue all the Runnables to an
* Executor. When all sub-parts are complete, the coordinating thread
* will be able to pass through await. (When threads must repeatedly
* count down in this way, instead use a {#link CyclicBarrier}.)
*
* <pre>
* class Driver2 { // ...
* void main() throws InterruptedException {
* CountDownLatch doneSignal = new CountDownLatch(N);
* Executor e = ...
*
* for (int i = 0; i < N; ++i) // create and start threads
* e.execute(new WorkerRunnable(doneSignal, i));
*
* doneSignal.await(); // wait for all to finish
* }
* }
*
* class WorkerRunnable implements Runnable {
* private final CountDownLatch doneSignal;
* private final int i;
* WorkerRunnable(CountDownLatch doneSignal, int i) {
* this.doneSignal = doneSignal;
* this.i = i;
* }
* public void run() {
* try {
* doWork(i);
* doneSignal.countDown();
* } catch (InterruptedException ex) {} // return;
* }
*
* void doWork() { ... }
* }
*
* </pre>
*
* <p>Memory consistency effects: Actions in a thread prior to calling
* {#code countDown()}
* <i>happen-before</i>
* actions following a successful return from a corresponding
* {#code await()} in another thread.
*
* #since 1.5
* #author Doug Lea
*/
public class ResettableCountDownLatch {
/**
* Synchronization control For CountDownLatch.
* Uses AQS state to represent count.
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
public final int startCount;
Sync(int count) {
this.startCount = count;
setState(startCount);
}
int getCount() {
return getState();
}
public int tryAcquireShared(int acquires) {
return getState() == 0? 1 : -1;
}
public boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
public void reset() {
setState(startCount);
}
}
private final Sync sync;
/**
* Constructs a {#code CountDownLatch} initialized with the given count.
*
* #param count the number of times {#link #countDown} must be invoked
* before threads can pass through {#link #await}
* #throws IllegalArgumentException if {#code count} is negative
*/
public ResettableCountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
/**
* Causes the current thread to wait until the latch has counted down to
* zero, unless the thread is {#linkplain Thread#interrupt interrupted}.
*
* <p>If the current count is zero then this method returns immediately.
*
* <p>If the current count is greater than zero then the current
* thread becomes disabled for thread scheduling purposes and lies
* dormant until one of two things happen:
* <ul>
* <li>The count reaches zero due to invocations of the
* {#link #countDown} method; or
* <li>Some other thread {#linkplain Thread#interrupt interrupts}
* the current thread.
* </ul>
*
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
* <li>is {#linkplain Thread#interrupt interrupted} while waiting,
* </ul>
* then {#link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* #throws InterruptedException if the current thread is interrupted
* while waiting
*/
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void reset() {
sync.reset();
}
/**
* Causes the current thread to wait until the latch has counted down to
* zero, unless the thread is {#linkplain Thread#interrupt interrupted},
* or the specified waiting time elapses.
*
* <p>If the current count is zero then this method returns immediately
* with the value {#code true}.
*
* <p>If the current count is greater than zero then the current
* thread becomes disabled for thread scheduling purposes and lies
* dormant until one of three things happen:
* <ul>
* <li>The count reaches zero due to invocations of the
* {#link #countDown} method; or
* <li>Some other thread {#linkplain Thread#interrupt interrupts}
* the current thread; or
* <li>The specified waiting time elapses.
* </ul>
*
* <p>If the count reaches zero then the method returns with the
* value {#code true}.
*
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
* <li>is {#linkplain Thread#interrupt interrupted} while waiting,
* </ul>
* then {#link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* <p>If the specified waiting time elapses then the value {#code false}
* is returned. If the time is less than or equal to zero, the method
* will not wait at all.
*
* #param timeout the maximum time to wait
* #param unit the time unit of the {#code timeout} argument
* #return {#code true} if the count reached zero and {#code false}
* if the waiting time elapsed before the count reached zero
* #throws InterruptedException if the current thread is interrupted
* while waiting
*/
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/**
* Decrements the count of the latch, releasing all waiting threads if
* the count reaches zero.
*
* <p>If the current count is greater than zero then it is decremented.
* If the new count is zero then all waiting threads are re-enabled for
* thread scheduling purposes.
*
* <p>If the current count equals zero then nothing happens.
*/
public void countDown() {
sync.releaseShared(1);
}
/**
* Returns the current count.
*
* <p>This method is typically used for debugging and testing purposes.
*
* #return the current count
*/
public long getCount() {
return sync.getCount();
}
/**
* Returns a string identifying this latch, as well as its state.
* The state, in brackets, includes the String {#code "Count ="}
* followed by the current count.
*
* #return a string identifying this latch, as well as its state
*/
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
}
Phaser has more options, we can implement resettable countdownLatch using that.
Please read below basic concepts from the following sites
https://examples.javacodegeeks.com/core-java/util/concurrent/phaser/java-util-concurrent-phaser-example/
http://netjs.blogspot.in/2016/01/phaser-in-java-concurrency.html
import java.util.concurrent.Phaser;
/**
* Resettable countdownLatch using phaser
*/
public class PhaserExample {
public static void main(String[] args) throws InterruptedException {
Phaser phaser = new Phaser(3); // you can use constructor hint or
// register() or mixture of both
// register self... so parties are incremented to 4 (3+1) now
phaser.register();
//register is one time call for all the phases.
//means no need to register for every phase
int phasecount = phaser.getPhase();
System.out.println("Phasecount is " + phasecount);
new PhaserExample().testPhaser(phaser, 2000);
new PhaserExample().testPhaser(phaser, 4000);
new PhaserExample().testPhaser(phaser, 6000);
// similar to await() in countDownLatch/CyclicBarrier
// parties are decremented to 3 (4+1) now
phaser.arriveAndAwaitAdvance();
// once all the thread arrived at same level, barrier opens
System.out.println("Barrier has broken.");
phasecount = phaser.getPhase();
System.out.println("Phasecount is " + phasecount);
//second phase
new PhaserExample().testPhaser(phaser, 2000);
new PhaserExample().testPhaser(phaser, 4000);
new PhaserExample().testPhaser(phaser, 6000);
phaser.arriveAndAwaitAdvance();
// once all the thread arrived at same level, barrier opens
System.out.println("Barrier has broken.");
phasecount = phaser.getPhase();
System.out.println("Phasecount is " + phasecount);
}
private void testPhaser(final Phaser phaser, final int sleepTime) {
// phaser.register(); //Already constructor hint is given so not
// required
new Thread() {
#Override
public void run() {
try {
Thread.sleep(sleepTime);
System.out.println(Thread.currentThread().getName() + " arrived");
// phaser.arrive(); //similar to CountDownLatch#countDown()
phaser.arriveAndAwaitAdvance();// thread will wait till Barrier opens
// arriveAndAwaitAdvance is similar to CyclicBarrier#await()
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " after passing barrier");
}
}.start();
}
}
Based on #Fidel -s answer, I made a drop-in replacement for ResettableCountDownLatch. The changes I made
mLatch is private volatile
mInitialCount is private final
the return type of the simple await() has changed to void.
Otherwise, the original code is cool too. So, this is the full, enhanced code:
public class ResettableCountDownLatch {
private final int initialCount;
private volatile CountDownLatch latch;
public ResettableCountDownLatch(int count) {
initialCount = count;
latch = new CountDownLatch(count);
}
public void reset() {
latch = new CountDownLatch(initialCount);
}
public void countDown() {
latch.countDown();
}
public void await() throws InterruptedException {
latch.await();
}
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
return latch.await(timeout, unit);
}
}
Update
Based on #Systemplanet-s comment, here is a safer version of reset():
// An atomic reference is required because reset() is not that atomic anymore, not even with `volatile`.
private final AtomicReference<CountDownLatch> latchHolder = new AtomicReference<>();
public void reset() {
// obtaining a local reference for modifying the required latch
final CountDownLatch oldLatch = latchHolder.getAndSet(null);
if (oldLatch != null) {
// checking the count each time to prevent unnecessary countdowns due to parallel countdowns
while (0L < oldLatch.getCount()) {
oldLatch.countDown();
}
}
}
Basically, it's a choice between simplicity and safety. I.e. if you are willing to move the responsibility to the client of your code, then it's enough to set the reference null in reset().
On the other hand, if you want to make it easy for the users of this code, then you need to use a little more tricks.
I'm not sure if this is fatally flawed but I recently had the same problem and solved it by simply instantiating a new CountDownLatch object each time I wanted to reset. Something like this:
Waiter:
bla();
latch.await();
//now the latch has counted down to 0
blabla();
CountDowner
foo();
latch.countDown();
//now the latch has counted down to 0
latch = new CountDownLatch(1);
Waiter.receiveReferenceToNewLatch(latch);
bar();
Obviously this is a heavy abstraction but thus far it has worked for me and doesn't require you to tinker with any class definitions.
Use Phaser.
if only one thread should to do work. U can join AtomicBoolean and Phaser
AtomicBoolean someConditionInProgress = new AtomicBoolean("false"); Phaser onConditionalPhaser = new Phaser(1);
(some function) if (!someConditionInProgress.compareAndSet(false, true)) {
try {
//do something
} finally {
someConditionInProgress.set(false);
//release barier
onConditionalPhaser.arrive();
}
} else {
onConditionalPhaser.awaitAdvance(onConditionalPhaser.getPhase());
}
Looks like you want to turn asynchronous I/O to synchronous. The whole idea of using asynchronous I/O is to avoid threads, but CountDownLatch requres using threads. This is an obvious contradiction in your question. So, you can:
keep using threads and employ synchronous I/O instead of Selectors and the suff. This will be much more simple and reliable
keep using asynchronous I/0 and give up CountDownLatch. Then you need an asynchronous library - look at RxJava, Akka, or df4j.
continue to develop your project for fun. Then you can try to use java.util.Semaphore instead of CountDownLatch, or program your own synchronization class using synchronized/wait/notify.
public class ResettableLatch {
private static final class Sync extends AbstractQueuedSynchronizer {
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return getState() == 0 ? 1 : -1;
}
public void reset(int count) {
setState(count);
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c - 1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
private final Sync sync;
public ResettableLatch(int count) {
if (count < 0)
throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public void countDown() {
sync.releaseShared(1);
}
public long getCount() {
return sync.getCount();
}
public void reset(int count) {
sync.reset(count);
}
}
This worked for me.
From what I was able to understand from the OP explanation and source code, the resettable CountDownLatch is not quite adequate concept for the problem he was going to solve. The documentation of the CountDownLatch itself mentions the OP's use case as simple gate initialized with a count of one:
CountDownLatch initialized with a count of one serves as a simple
on/off latch, or gate: all threads invoking await wait at the gate
until it is opened by a thread invoking countDown.
, but CountDownLatch implementation does not go any further in this direction.
So, myself having a problem similar to that of OP's I decided to introduce a SimpleGate class with the following properties:
Number of permits is one, which means it can be either in On or Off state;
There is a dedicated thread, called Gate Keeper that is only allowed to shut off or open up the Gate;
The right of gate keeping is transferable;
the opening up the Gate immediately allows the threads, that tried to come through the Gate, to do it (this very logical feature has been overlooked in the other answers);
as the thread contention is expected to be high, fairness is supported as an option, this allows to decrease an effect of thread barging.
public class SimpleGate {
private static class Sync extends AbstractQueuedSynchronizer {
// State
private static final int SHUT = 1;
private static final int OPEN = 0;
private boolean fair;
public void setFair(boolean fair) {
this.fair = fair;
}
public void shutOff() {
super.setState(SHUT);
}
#Override
protected int tryAcquireShared(int arg) {
if (fair && super.hasQueuedPredecessors())
return -1;
return super.getState() == OPEN ? 1 : -1;
}
#Override
protected boolean tryReleaseShared(int arg) {
super.setState(OPEN);
return true;
}
}
private Sync sync = new Sync();
private volatile Thread gateKeeper = Thread.currentThread();
public SimpleGate(){
this(true);
}
public SimpleGate(boolean shutOff){
this(shutOff, false);
}
public SimpleGate(boolean shutOff, boolean fair){
if (shutOff)
sync.shutOff();
sync.setFair(fair);
}
public void comeThrough(){
if (Thread.currentThread() == gateKeeper)
throw new IllegalStateException("Gate Keeper thread is not supposed to come through the gate");
sync.acquireShared(0);
}
public void shutOff(){
if (Thread.currentThread() != gateKeeper)
throw new IllegalStateException("Only a Gate Keeper thread is allowed to shut off");
sync.shutOff();
}
public void openUp(){
if (Thread.currentThread() != gateKeeper)
throw new IllegalStateException("Only a Gate Keeper thread is allowed to open up");
sync.releaseShared(0);
}
public void transferOwnership(Thread newGateKeeper){
this.gateKeeper = newGateKeeper;
}
// an addition of waiting interruptibly and waiting for specified amount of time,
//if they are needed, is trivial
}
Another drop-in replacement
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class ResettableCountDownLatch {
int mInitialCount;
CountDownLatch mLatch;
public ResettableCountDownLatch(int count) {
mInitialCount = count;
mLatch = new CountDownLatch(count);
}
public void reset() {
mLatch = new CountDownLatch(mInitialCount);
}
public void countDown() {
mLatch.countDown();
}
public boolean await() throws InterruptedException {
boolean result = mLatch.await();
return result;
}
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
boolean result = mLatch.await(timeout, unit);
return result;
}
}
Related
Intellij IDEA is complaining about Generic array creation
public abstract class BaseImageLoader<CacheItem>
{
private ImageLoaderThread[] workerThreads;
public BaseImageLoader(Context context)
{
...
workerThreads = new ImageLoaderThread[DEFAULT_CACHE_THREAD_POOL_SIZE];//Generic array creation error
...
}
}
ImageLoaderThread is in fact a subclass of java.lang.Thread, its not generic
I dont get what im doing wrong
this works fine:
Thread[] threads = new Thread[DEFAULT_CACHE_THREAD_POOL_SIZE];
ImageLoaderThread class
private class ImageLoaderThread extends Thread
{
/**
* The queue of requests to service.
*/
private final BlockingQueue<ImageData> mQueue;
/**
* Used for telling us to die.
*/
private volatile boolean mQuit = false;
/**
* Creates a new cache thread. You must call {#link #start()}
* in order to begin processing.
*
* #param queue Queue of incoming requests for triage
*/
public ImageLoaderThread(BlockingQueue<ImageData> queue)
{
mQueue = queue;
}
/**
* Forces this thread to quit immediately. If any requests are still in
* the queue, they are not guaranteed to be processed.
*/
public void quit()
{
mQuit = true;
interrupt();
}
#Override
public void run()
{
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
ImageData data;
while (true)
{
try
{
// Take a request from the queue.
data = mQueue.take();
}
catch (InterruptedException e)
{
// We may have been interrupted because it was time to quit.
if (mQuit)
{
return;
}
continue;
}
...
//other unrelated stuff
}
}
}
After spending lots of time with threadpool concepts and by reading different codes on numbers of blogs and posting questions on Stackoverflow.com, now I got clear image of this concept. But in the meanwhile, I found some doubts in code.
When pool.assign(new TestWorkerThread()); executes in TestThreadPool Class, it calls
done.workerBegin(); method that is in Done Class, where it increments _activeThreads variable. But what I thinks is, LOGICALLY that is not correct because if number of threads are less(in this case 2) than number of tasks (given in TestThreadPool Class)(in this case 5), it increments _activeThreads (i.e., _activeThreads = 5) counts unnecessarily.
What _started variable does in Done class?
How waitDone() and waitBegin() (in Done Class ) performs their functioning? (It is good if you explain these two methods step by step.)
Code is as follows. I am arranging the codes according to its flow.
TestThreadPool Class :-
package hitesh;
/**
*
* #author jhamb
*/
public class TestThreadPool {
public static void main(String args[]) throws InterruptedException
{
ThreadPool pool = new ThreadPool(2);
for (int i = 1;i <= 5;i++) {
pool.assign(new TestWorkerThread());
}
System.out.println("All tasks are assigned");
pool.complete();
System.out.println("All tasks are done.");
}
}
TestWorkerThread Class :-
package hitesh;
/**
*
* #author jhamb
*/
/**
* This class shows an example worker thread that can
* be used with the thread pool. It demonstrates the main
* points that should be included in any worker thread. Use
* this as a starting point for your own threads.
*/
public class TestWorkerThread implements Runnable {
static private int count = 0;
private int taskNumber;
protected Done done;
/**
*
* #param done
*/
TestWorkerThread()
{
count++;
taskNumber = count;
//System.out.println("tasknumber ---> " + taskNumber);
}
public void run()
{
System.out.println("TWT run starts --> " + this.toString());
for (int i=0;i <= 100;i += 25) {
System.out.println("Task number: " + taskNumber +
",percent complete = " + i );
try {
Thread.sleep((int)(Math.random()*500));
} catch (InterruptedException e) {
}
}
System.out.println("task for thread --> " + this.toString() + " completed");
}
}
ThreadPool Class :-
package hitesh;
/**
*
* #author jhamb
*/
import java.util.*;
/*
* This is the main class for the thread pool. You should
* create an instance of this class and assign tasks to it.
*/
public class ThreadPool {
protected Thread threads[] = null;
Collection assignments = new ArrayList(3);
protected Done done = new Done();
public ThreadPool(int size) throws InterruptedException
{
threads = new WorkerThread[size];
for (int i=0;i<threads.length;i++) {
threads[i] = new WorkerThread(this);
threads[i].start();
System.out.println ("thread " + i + " started");
threads[i].sleep(1000);
}
}
public synchronized void assign(Runnable r)
{
done.workerBegin();
assignments.add(r);
System.out.println("Collection size ---> " + assignments.size() + " Thread can work on this");
notify();
}
public synchronized Runnable getAssignment()
{
try {
while ( !assignments.iterator().hasNext() )
wait();
Runnable r = (Runnable)assignments.iterator().next();
assignments.remove(r);
return r;
} catch (InterruptedException e) {
done.workerEnd();
return null;
}
}
public void complete()
{
done.waitBegin();
done.waitDone();
}
}
WorkerThread Class :-
package hitesh;
import java.util.*;
/**
*
* #author jhamb
*/
/**
* The worker threads that make up the thread pool.
*/
class WorkerThread extends Thread {
/**
* True if this thread is currently processing.
*/
public boolean busy;
/**
* The thread pool that this object belongs to.
*/
public ThreadPool owner;
/**
* The constructor.
*
* #param o the thread pool
*/
WorkerThread(ThreadPool o)
{
owner = o;
}
/**
* Scan for and execute tasks.
*/
//#Override
public void run()
{
System.out.println("Threads name : "+ this.getName() + " working.....");
Runnable target = null;
do {
System.out.println("enter in do while " + this.getName() );
target = owner.getAssignment();
System.out.println("GetAssignment k aage aa gya mai " + target);
if (target!=null) {
target.run();
//target.
owner.done.workerEnd();
}
} while (target!=null);
System.out.println("do while finishes for "+ this.getName());
}
}
Done Class :-
package hitesh;
/**
*
* #author jhamb
*/
/**
*
* This is a thread pool for Java, it is
* simple to use and gets the job done. This program and
* all supporting files are distributed under the Limited
* GNU Public License (LGPL, http://www.gnu.org).
*
* This is a very simple object that
* allows the TheadPool to determine when
* it is done. This object implements
* a simple lock that the ThreadPool class
* can wait on to determine completion.
* Done is defined as the ThreadPool having
* no more work to complete.
*
* Copyright 2001 by Jeff Heaton
*
* #author Jeff Heaton (http://www.jeffheaton.com)
* #version 1.0
*/
public class Done {
/**
* The number of Worker object
* threads that are currently working
* on something.
*/
private int _activeThreads = 0;
/**
* This boolean keeps track of if
* the very first thread has started
* or not. This prevents this object
* from falsely reporting that the ThreadPool
* is done, just because the first thread
* has not yet started.
*/
private boolean _started = false;
/**
* This method can be called to block
* the current thread until the ThreadPool
* is done.
*/
synchronized public void waitDone()
{
try {
while ( _activeThreads>0 ) {
wait();
}
} catch ( InterruptedException e ) {
}
}
/**
* Called to wait for the first thread to
* start. Once this method returns the
* process has begun.
*/
synchronized public void waitBegin()
{
try {
while ( !_started ) {
wait();
}
} catch ( InterruptedException e ) {
}
}
/**
* Called by a Worker object
* to indicate that it has begun
* working on a workload.
*/
synchronized public void workerBegin()
{
_activeThreads++;
_started = true;
notify();
}
/**
* Called by a Worker object to
* indicate that it has completed a
* workload.
*/
synchronized public void workerEnd()
{
_activeThreads--;
notify();
}
/**
* Called to reset this object to
* its initial state.
*/
synchronized public void reset()
{
_activeThreads = 0;
}
}
Please help. Thanks in advance. Looking for your kind response.
Now I understand that whole code very perfectly. If you find any doubts in this code, then you can ask.
Answers of my questions are as follows after reading a lot on this.
Yes, you are right, it is logically wrong. Its better, if it would be _activeTasks . It is used to kill all the threads , when threadpool have no more work because waitDone() function executes successfully only when _activeTasks <= 0.
This Variable is used in waitBegin() method. Whenever any tasks starts, it updates _started by TRUE, means the tasks that are assigned by users is now in processing by threads, means threads starts working on these tasks. If tasks is not given by user, then all threads are still active , and waiting for tasks. This is the use of this variable here.
waitBegin() method executes successfully when threads starts working on tasks, because in that case only _started become true. Otherwise, threads keep on waiting for some tasks. waitDone() executes successfully only when _activeTasks become Zero, because this is the only situation when threadpool don't have any work to perform, means threadpool completes its work. Otherwise, it keep waiting until all tasks finish, means it waits until when _activeTasks becomes ZERO
My Problem is solved. Here is the code:
SwingWorker class:
package ths.turnier;
import javax.swing.SwingUtilities;
/**
* This is the 3rd version of SwingWorker (also known as
* SwingWorker 3), an abstract class that you subclass to
* perform GUI-related work in a dedicated thread. For
* instructions on using this class, see:
*
* http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html
*
* Note that the API changed slightly in the 3rd version:
* You must now invoke start() on the SwingWorker after
* creating it.
*/
public abstract class SwingWorker {
private Object value; // see getValue(), setValue()
private Thread thread;
/**
* Class to maintain reference to current worker thread
* under separate synchronization control.
*/
private static class ThreadVar {
private Thread thread;
ThreadVar(Thread t) { thread = t; }
synchronized Thread get() { return thread; }
synchronized void clear() { thread = null; }
}
private ThreadVar threadVar;
/**
* Get the value produced by the worker thread, or null if it
* hasn't been constructed yet.
*/
protected synchronized Object getValue() {
return value;
}
/**
* Set the value produced by worker thread
*/
private synchronized void setValue(Object x) {
value = x;
}
/**
* Compute the value to be returned by the <code>get</code> method.
*/
public abstract Object construct();
/**
* public void run() { finished(); } Called on the event dispatching thread (not on the worker thread)
* after the <code>construct</code> method has returned.
*/
public void finished() {
}
/**
* A new method that interrupts the worker thread. Call this method
* to force the worker to stop what it's doing.
*/
public void interrupt() {
Thread t = threadVar.get();
if (t != null) {
t.interrupt();
}
threadVar.clear();
}
/**
* Return the value created by the <code>construct</code> method.
* Returns null if either the constructing thread or the current
* thread was interrupted before a value was produced.
*
* #return the value created by the <code>construct</code> method
*/
public Object get() {
while (true) {
Thread t = threadVar.get();
if (t == null) {
return getValue();
}
try {
t.join();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt(); // propagate
return null;
}
}
}
/**
* Start a thread that will call the <code>construct</code> method
* and then exit.
*/
public SwingWorker() {
final Runnable doFinished = new Runnable() {
public void run() { finished(); }
};
Runnable doConstruct = new Runnable() {
public void run() {
try {
setValue(construct());
}
finally {
threadVar.clear();
}
SwingUtilities.invokeLater(doFinished);
}
};
Thread t = new Thread(doConstruct);
threadVar = new ThreadVar(t);
}
/**
* Start the worker thread.
*/
public void start() {
Thread t = threadVar.get();
if (t != null) {
t.start();
}
}
}
MonitoredInputStream:
/**
* A class that monitors the read progress of an input stream.
*
* #author Hermia Yeung "Sheepy"
* #since 2012-04-05 18:42
*/
public class MonitoredInputStream extends FilterInputStream {
private volatile long mark = 0;
private volatile long lastTriggeredLocation = 0;
private volatile long location = 0;
private final int threshold;
private final List<ChangeListener> listeners = new ArrayList<ChangeListener>(4);
/**
* Creates a MonitoredInputStream over an underlying input stream.
* #param in Underlying input stream, should be non-null because of no public setter
* #param threshold Min. position change (in byte) to trigger change event.
*/
public MonitoredInputStream(InputStream in, int threshold) {
super(in);
this.threshold = threshold;
}
/**
* Creates a MonitoredInputStream over an underlying input stream.
* Default threshold is 16KB, small threshold may impact performance impact on larger streams.
* #param in Underlying input stream, should be non-null because of no public setter
*/
public MonitoredInputStream(InputStream in) {
super(in);
this.threshold = 1024*16;
}
public void addChangeListener(ChangeListener l) { if (!listeners.contains(l)) listeners.add(l); }
public void removeChangeListener(ChangeListener l) { listeners.remove(l); }
public long getProgress() { return location; }
protected void triggerChanged( final long location ) {
if ( threshold > 0 && Math.abs( location-lastTriggeredLocation ) < threshold ) return;
lastTriggeredLocation = location;
if (listeners.size() <= 0) return;
try {
final ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener l : listeners) l.stateChanged(evt);
} catch (ConcurrentModificationException e) {
triggerChanged(location); // List changed? Let's re-try.
}
}
#Override public int read() throws IOException {
final int i = super.read();
if ( i != -1 ) triggerChanged( location++ );
return i;
}
#Override public int read(byte[] b, int off, int len) throws IOException {
final int i = super.read(b, off, len);
if ( i > 0 ) triggerChanged( location += i );
return i;
}
#Override public long skip(long n) throws IOException {
final long i = super.skip(n);
if ( i > 0 ) triggerChanged( location += i );
return i;
}
#Override public void mark(int readlimit) {
super.mark(readlimit);
mark = location;
}
#Override public void reset() throws IOException {
super.reset();
if ( location != mark ) triggerChanged( location = mark );
}
}
How to use this:
void updateProgressWknAdd(final int i)
{
Runnable doSetProgress = new Runnable() {
public void run() {
progressWknAdd.setValue(i);
}
};
SwingUtilities.invokeLater(doSetProgress);
}
Object doWorkWkn(String pfad) {
// Code which reads file + setting the max of jprogressbar to file size. and:
final MonitoredInputStream mis = new MonitoredInputStream(fis);
mis.addChangeListener( new ChangeListener() { #Override public void stateChanged(ChangeEvent e) {
updateProgressWknRead((int) mis.getProgress());
}});
}
How to use the worker:
SwingWorker worker = new SwingWorker() {
public Object construct() {
return doWorkWkn(pfad_zur_datei);
}
public void finished() {
frame_progressbar.setVisible(false);
}
};
worker.start();
The swingWorker runs doWorkWkn until it's finished. After that a jFrame is set non-visible.
The doWorkWkn() reads the file and adds a changeListener, which updates the progressbar on every change.
Swing is NOT thread safe.
The UI doesn't update because you are blocking the EventDispatchingThread with the reading of the file. This is preventing the progress bar from been updated on the screen.
As #LanguagesNamedAfterCofee suggested, you should use a SwingWorker to perform the actual reading of the file and allow it's update methods (publish and setProgress) to update the UI.
Somewhere in the code you are using you use a diamond operator that is <> You have to resolve this to the matching generic.
Like transform List<String> foo = new ArrayList<>(); to List<String> foo = new ArrayList<String>();
Just search for the <> occurrence and resolve it.
I have written a game of life for programming practice. There are 3 different implementations of the generator. First: One main thread + N sub threads, Second: SwingWorker + N sub threads, Third: SwingWorker + ExecutorService.
N is the number of availableProcessors or user defined.
The first two implementations runs fine, with one and more threads.
The implementation with the ExecutorServise runs fine with one thread, but locks with more than one. I tried everything, but i can't get the solution.
Here the code of the fine workling implementation (second one):
package example.generator;
import javax.swing.SwingWorker;
/**
* AbstractGenerator implementation 2: SwingWorker + sub threads.
*
* #author Dima
*/
public final class WorldGenerator2 extends AbstractGenerator {
/**
* Constructor.
* #param gamePanel The game panel
*/
public WorldGenerator2() {
super();
}
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startGenerationProcess()
*/
#Override
protected void startGenerationProcess() {
final SwingWorker<Void, Void> worker = this.createWorker();
worker.execute();
}
/**
* Creates a swing worker for the generation process.
* #return The swing worker
*/
private SwingWorker<Void, Void> createWorker() {
return new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws InterruptedException {
WorldGenerator2.this.generationProcessing();
return null;
}
};
}
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startFirstStep()
*/
#Override
public void startFirstStep() throws InterruptedException {
this.getQueue().addAll(this.getLivingCells());
for (int i = 0; i < this.getCoresToUse(); i++) {
final Thread thread = new Thread() {
#Override
public void run() {
WorldGenerator2.this.fistStepProcessing();
}
};
thread.start();
thread.join();
}
}
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startSecondStep()
*/
#Override
protected void startSecondStep() throws InterruptedException {
this.getQueue().addAll(this.getCellsToCheck());
for (int i = 0; i < this.getCoresToUse(); i++) {
final Thread thread = new Thread() {
#Override
public void run() {
WorldGenerator2.this.secondStepProcessing();
}
};
thread.start();
thread.join();
}
}
}
Here is the code of the not working implementation with executor service:
package example.generator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.SwingWorker;
/**
* AbstractGenerator implementation 3: SwingWorker + ExecutorService.
*
* #author Dima
*/
public final class WorldGenerator3 extends AbstractGenerator {
private CountDownLatch countDownLatch;
private ExecutorService executor;
/**
* Constructor.
* #param gamePanel The game panel
*/
public WorldGenerator3() {
super();
}
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startGenerationProcess()
*/
#Override
protected void startGenerationProcess() {
this.executor = Executors.newFixedThreadPool(this.getCoresToUse());
final SwingWorker<Void, Void> worker = this.createWorker();
worker.execute();
}
/**
* Creates a swing worker for the generation process.
* #return The swing worker
*/
private SwingWorker<Void, Void> createWorker() {
return new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws InterruptedException {
WorldGenerator3.this.generationProcessing();
return null;
}
};
}
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startFirstStep()
*/
#Override
public void startFirstStep() throws InterruptedException {
this.getQueue().addAll(this.getLivingCells());
this.countDownLatch = new CountDownLatch(this.getCoresToUse());
for (int i = 0; i < this.getCoresToUse(); i++) {
this.executor.execute(new Runnable() {
#Override
public void run() {
WorldGenerator3.this.fistStepProcessing();
WorldGenerator3.this.countDownLatch.countDown();
}
});
}
this.countDownLatch.await();
}
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startSecondStep()
*/
#Override
protected void startSecondStep() throws InterruptedException {
this.getQueue().addAll(this.getCellsToCheck());
this.countDownLatch = new CountDownLatch(this.getCoresToUse());
for (int i = 0; i < this.getCoresToUse(); i++) {
this.executor.execute(new Runnable() {
#Override
public void run() {
WorldGenerator3.this.secondStepProcessing();
WorldGenerator3.this.countDownLatch.countDown();
}
});
}
this.countDownLatch.await();
}
}
Here you can download, a sample of my application, with a small launcher. it prints only the result of a iteration on the console: Link
Now my code looks like this:
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startFirstStep()
*/
#Override
public void startFirstStep() throws InterruptedException {
this.getQueue().addAll(this.getLivingCells());
final ArrayList<Callable<Void>> list = new ArrayList<Callable<Void>>(this.getCoresToUse());
for (int i = 0; i < this.getCoresToUse(); i++) {
list.add(new Callable<Void>() {
#Override
public Void call() throws Exception {
WorldGenerator3.this.fistStepProcessing();
return null;
}
}
);
}
this.executor.invokeAll(list);
}
But here is again the same problem. If I run it with one core (thread) there are no problems. If I set the number of cores to more than one, it locks. In my first question there is a link to a example, which you can run (in eclipse). Maybe I overlook something in the previous code.
I find your usage of Executors facilities a little bit odd...
I.e. the idea is to have Executor with a pool of threads, size of which usually is related to number of cores your CPU supports.
Then you submit whatever number of parallel tasks to the Executor, letting it to decide what to execute when and on which available Thread from its pool.
As for the CountDownLatch... Why not use ExecutorService.invokeAll? This method will block untill all submitted tasks are completed or timeout is reached. So it will do counting of the work left on your behalf.
Or a CompletionService which "decouples the production of new asynchronous tasks from the consumption of the results of completed tasks" if you want to consume Task result as soon as it becomes available i.e. not wait for all tasks to complete first.
Something like
private static final int WORKER_THREAD_COUNT_DEFAULT = Runtime.getRuntime().availableProcessors() * 2;
ExecutorService executor = Executors.newFixedThreadPool(WORKER_THREAD_COUNT);
// your tasks may or may not return result so consuming invokeAll return value may not be necessary in your case
List<Future<T>> futuresResult = executor.invokeAll(tasksToRunInParallel, EXECUTE_TIMEOUT,
TimeUnit.SECONDS);
In all variants you are executing threads in serial rather than parallel because you join and await inside the for-loop. That means that the for-loop cannot move on to the next iteration until the thread just started is complete. This amounts to having only one thread live at any given time -- either the main thread or the one thread created in the current loop iteration. If you want to join on multiple threads, you must collect the refs to them and then, outside the loop where you started them all, enter another loop where you join on each one.
As for using CountDownLatch in the Executors variant, what was said for threads goes for the latch here: don't use an instance var; use a local list that collects all latches and await them in a separate loop.
But, you shouldn't really be using the CountDownLatch in the first place: you should put all your parallel tasks in a list of Callables and call ExecutorService.invokeAll with it. It will automatically block until all the tasks are done.
I have seen the thread pool executor implementation and the rejected execution policies that it provides. However, I have a custom requirement - I want to have a call back mechanism where in I get notifications when the queue size limit is reached and say when the queue size reduces to say 80 % of the max allowed queue size.
public interface ISaturatedPoolObserver {
void onSaturated(); // called when the blocking queue reaches the size limit
void onUnsaturated(); // called when blocking queues size goes below the threshold.
}
I feel that this can be implemented by subclassing thread pool executor, but is there an already implemented version? I would be happy to add more details and my work so far as and when needed to provide clarity.
I want to have a call back mechanism where in I get notifications when the queue size limit is reached...
I wouldn't subclass the executor but I would subclass the BlockingQueue that is used by the executor. Something like the following should work. There are race conditions in the code around the checkUnsaturated() if you remove an entry and someone puts one back in. You might have to synchronize on the queue if these need to be perfect. Also, I have no idea what methods the executor implementations use so you might not need to override some of these.
public class ObservableBlockingQueue<E> extends LinkedBlockingQueue<E> {
private ISaturatedPoolObserver observer;
private int capacity;
public ObservableBlockingQueue(ISaturatedPoolObserver observer,
int capacity) {
super(capacity);
this.observer = observer;
this.capacity = capacity;
}
#Override
public boolean offer(E o) {
boolean offered = super.offer(o);
if (!offered) {
observer.onSaturated();
}
return offered;
}
#Override
public boolean offer(E o, long timeout, TimeUnit unit) throws InterruptedException {
boolean offered = super.offer(o, timeout, unit);
if (!offered) {
observer.onSaturated();
}
return offered;
}
#Override
public E poll() {
E e = super.poll();
if (e != null) {
checkUnsaturated();
}
return e;
}
#Override
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E e = super.poll(timeout, unit);
if (e != null) {
checkUnsaturated();
}
return e;
}
#Override
public E take() throws InterruptedException {
E e = super.take();
checkUnsaturated();
return e;
}
#Override
public boolean remove(E e) throws InterruptedException {
boolean removed = super.remove(e);
if (removed) {
checkUnsaturated();
}
return removed;
}
private void checkUnsaturated() {
if (super.size() * 100 / capacity < UNSATURATED_PERCENTAGE) {
observer.onUnsaturated();
}
}
}
So here is the code that I have based on the answer above. The call to saturated and unSaturated needs to be invoked during sustained load on the worker queue of the thread pool and I believe the implementation achieves it by making use of non blocking algorithm.
Also, this implementation can be used for any implementation of blocking queue (also the original queue could be bounded or unbounded).
I am using guava's ForwardingBlockingQueue to write my decorator. Any suggestions would be greatly appreciated.
import java.util.Collection;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import com.google.common.util.concurrent.ForwardingBlockingQueue;
/**
* #version $Id$
* #param <E> the type of elements held in this blocking queue.
*/
public class BoundObservableBlockingQueue<E> extends ForwardingBlockingQueue<E> {
/** observer to receive callbacks. */
private final ISaturatedQueueObserver queueBoundObserver;
/** original blocking queue being decorated. */
private final BlockingQueue<E> queueDelegate;
/** user specified blocking queue bound capacity. */
private final int boundCapacity;
/** user specified blocking queue bound capacity. */
private final int boundThreshold;
/** flag to represent the saturated state of the queue. */
private final AtomicBoolean isSaturated = new AtomicBoolean(false);
/**
*
* #param pQueue {#link BlockingQueue
* #param pQueueBoundObserver {#link ISaturatedQueueObserver}
* #param pBoundCapacity saturation capacity for the bound queue.
*/
public BoundObservableBlockingQueue(final BlockingQueue<E> pQueue,
final ISaturatedQueueObserver pQueueBoundObserver, final int pBoundCapacity) {
queueDelegate = pQueue;
queueBoundObserver = pQueueBoundObserver;
boundCapacity = pBoundCapacity;
boundThreshold = (int) 0.8 * pBoundCapacity;
}
/** {#inheritDoc} */
#Override
public final boolean offer(final E e) {
boolean isOffered = delegate().offer(e);
checkSaturated();
return isOffered;
}
/** {#inheritDoc} */
#Override
public final boolean offer(final E e, final long timeout, final TimeUnit unit) throws InterruptedException {
boolean isOffered = delegate().offer(e, timeout, unit);
checkSaturated();
return isOffered;
}
/** {#inheritDoc} */
#Override
public final E remove() {
E element = delegate().remove();
checkUnsaturated();
return element;
}
/** {#inheritDoc} */
#Override
public final E poll() {
E element = delegate().poll();
checkUnsaturated();
return element;
}
/** {#inheritDoc} */
#Override
public final E poll(final long timeout, final TimeUnit unit) throws InterruptedException {
E element = delegate().poll(timeout, unit);
checkUnsaturated();
return element;
}
/** {#inheritDoc} */
#Override
public final E take() throws InterruptedException {
E element = delegate().take();
checkUnsaturated();
return element;
}
/** {#inheritDoc} */
#Override
public final boolean remove(final Object o) {
boolean isRemoved = delegate().remove(o);
checkUnsaturated();
return isRemoved;
}
/** {#inheritDoc} */
#Override
protected final BlockingQueue<E> delegate() {
return queueDelegate;
}
// thread pool uses this only during invocation of shutdown; in which cases call to unSaturated isn't needed because
// the queue is no longer ready to accept any more records.
/** {#inheritDoc} */
#Override
public final int drainTo(final Collection<? super E> c) {
return delegate().drainTo(c);
}
private void checkUnsaturated() {
if (delegate().size() < boundThreshold && isSaturated.get()) {
if (isSaturated.compareAndSet(true, false)) {
queueBoundObserver.onUnsaturated();
}
}
}
private void checkSaturated() {
if ((delegate().size() >= boundCapacity) && !isSaturated.get()) {
if (isSaturated.compareAndSet(false, true)) {
queueBoundObserver.onSaturated();
}
}
}
}