Using a jProgressBar with SaxParser - java

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.

Related

Error in run() method when using Thread.sleep()

I am trying to run an Elevator instance in its own thread. The run() method is being called, but there must be some error in my code that is preventing it from running. My code compiles and runs without errors, but the "Elevator starting up" is never printed.
I think it has to do with Thread.sleep() needing to throw an InterruptedException. I tried to try/catch the exception since Runnable doesn't throw an InterruptedException.
Can you help me figure out why it isn't running?
Here is my run() method:
#Override
public void run() {
System.out.printf("Elevator starting up");
while(true) {
try {
if (currentDirection == Direction.UP) {
this.moveUp();
}
else if (currentDirection == Direction.DOWN) {
this.moveDown();
}
else {Thread.sleep(250);}
}
catch (InterruptedException ie) {
System.out.println("Elevator has experienced a critical error")
}
}
}
Here is my start() method within the Elevator class. This is called from main for each elevator in the building.
public void start() {
activeThread = new Thread();
activeThread.start();
}
The moveUp() method:
public void moveUp() throws InterruptedException {
Thread.sleep(travelSpeed);
setCurrentFloor(currentFloor++);
}
The moveDown() method:
public void moveDown() throws InterruptedException{
Thread.sleep(travelSpeed);
setCurrentFloor(currentFloor--);
}
Full PassengerElevator.class code
public class PassengerElevator implements ElevatorMover, Runnable {
private final int elevID; // elevator number
private final int maxCapacity; // max capacity of the elevator
private int currentCapacity; // the current capacity of the elevator
private final long travelSpeed; // length of travel time between floors
private final long doorSpeed; // length of time door stays open
private int currentFloor; // the current floor the elevator is on
private final int defaultFloor; // the default floor after timeout
private Direction currentDirection; // the current direction the elevator is moving
public Thread activeThread = null; // contains the instance of an elevator thread
/**
* Constructor
* #param elevID the ID number, as an int, given to the elevator
*/
public PassengerElevator(int elevID) {
this.elevID = elevID;
maxCapacity = 10;
currentCapacity = 0;
travelSpeed = 500; // in milliseconds
doorSpeed = 500; // in milliseconds
currentFloor = 1;
defaultFloor = 1;
currentDirection = Direction.IDLE;
}
/**
* makes the elevator go up one floor. Takes travelSpeed time
* #throws InterruptedException
*/
#Override
public void moveUp() throws InterruptedException {
Thread.sleep(travelSpeed);
setCurrentFloor(currentFloor++);
}
/**
* makes the elevator go down one floor. Takes travelSpeed time
* #throws InterruptedException
*/
#Override
public void moveDown() throws InterruptedException{
Thread.sleep(travelSpeed);
setCurrentFloor(currentFloor--);
}
/**
* makes the elevator door open for doorSpeed time. When door is open people
* move into elevator
* #throws InterruptedException
*/
#Override
public void openDoors() throws InterruptedException{
Thread.sleep(doorSpeed);
}
public int getElevID() {
return elevID;
}
private int getMaxCapacity() {
return maxCapacity;
}
private int getCurrentCapacity() {
return currentCapacity;
}
private void setCurrentCapacity(int x) {
currentCapacity = x;
}
private double getTravelSpeed() {
return travelSpeed;
}
private double getDoorSpeed() {
return doorSpeed;
}
public int getCurrentFloor() {
return currentFloor;
}
private void setCurrentFloor(int x) {
currentFloor = x;
}
private int getDefaultFloor() {
return defaultFloor;
}
private void setCurrentDirection(Direction x) {
currentDirection = x;
}
private Direction getCurrentDirection() {
return currentDirection;
}
/**
* Starts a new thread for an elevator instance to run in
*/
public void start() {
activeThread = new Thread();
activeThread.start();
}
/**
* The running loop for an elevator instance. Client will change current direction
* and use the currentFloor as a check.
*/
#Override
public void run() {
System.out.printf("Elevator starting up");
while(true) {
try {
if (currentDirection == Direction.UP) {
this.moveUp();
}
else if (currentDirection == Direction.DOWN) {
this.moveDown();
}
else {Thread.sleep(250);}
}
catch (InterruptedException ie) {
System.out.println("Elevator has experienced a critical error")
}
}
}
}
You are creating a Thread instance directly, and since it is the plain Java Thread class, it has no code in the run method. That means that when you start it, it does nothing. This is the relevant code:
public void start() {
activeThread = new Thread();
activeThread.start();
}
You need to start a thread that is going to run your code. Either make your elevator class extend Thread or implement Runnable.
When extending Thread:
thread = new Elevator();
thread.start();
When implementing Runnable:
thread = new Thread(new Elevator());
thread.start();
The Thread documentation provides examples of usage.

Generic array creation for java.lang.Thread

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
}
}
}

How to wake up RecursiveTask<V> with notify?

I cannot figure it out, how can I wake up RecursiveTasks, invoked by ForkJoinPool, when these tasks are on hold by wait method. Here is my simple example with the method MainRecursionClass.resume which is incorrect (does not wakes up RecursiveTasks).
public class Program {
public static void main(String[] args) {
Program p = new Program();
final MainRecursionClass mrc = p.new MainRecursionClass();
//Thread outputs integers to simulate work
new Thread() {
public void run() {
mrc.doJob();
}
}.start();
//Thread performs wait and notify on MainRecursionClass object
p.new PauseResume(mrc).start();
}
/**
*
* This class performs suspend and resume operations to the MainRecursionClass class object
*
*/
private class PauseResume extends Thread {
private MainRecursionClass rv;
public PauseResume(MainRecursionClass rv) {
this.rv = rv;
}
#Override
public void run() {
while(!isInterrupted()) {
try {
sleep(4000);
rv.suspend();
sleep(8000);
rv.resume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private class MainRecursionClass {
private boolean pause = false;
private MyRecursive rv;
public void doJob() {
rv = new MyRecursive(0, 100000);
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(rv);
}
public void suspend() {
pause = true;
System.out.println("Suspended");
}
/**
* This method is incorrect. It should wake up all MyRecursive instances to continue their work.
*/
public synchronized void resume() {
pause = false;
notifyAll();
System.out.println("Resumed");
}
private class MyRecursive extends RecursiveTask<Object> {
private static final long serialVersionUID = 1L;
private int start;
private int length;
private int threshold = 15;
public MyRecursive(int start, int length) {
super();
this.start = start;
this.length = length;
}
protected void computeDirectly() throws Exception {
for (int index = start; index < start + length; index++) {
//PAUSE
synchronized (this) {
try {
while(pause) {
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//PAUSE
//some output to simulate work...
System.out.println(index);
Thread.sleep(1000);
}
}
/**
* Recursion
*/
#Override
protected Object compute() {
if (length <= threshold) {
try {
computeDirectly();
} catch (Exception e) {
return e;
}
return null;
}
int split = length / 2;
invokeAll(new MyRecursive(start, split),
new MyRecursive(start + split, length - split));
return null;
}
}
}
}
You should not use wait/notify in the tasks running on a thread pool. If your pool is bounded, then it may cause thread starvation (a form of deadlock). If it is unbounded, too many threads can be created and main memory exhausted.
Instead, you should split your task in 2 (or more) and start subtasks according to their starting conditions. When you want a task to wait(), then refactor it so that current subtask exits, and next subtask is prepared to run.
Finally I came to this solution: I created List<MyRecursive> list = new ArrayList<>(); object in MainRecursionClass and added every MyRecursive instance, created recursively, in the list. The class MyRecursive has new method:
public synchronized void resume() {
notify();
}
When, the method MainRecursionClass.resume(), which wakes up the threads, looks like this:
public void resume() {
System.out.println("Resumed");
pause = false;
for(MyRecursive mr : list) {
if(mr != null)
mr.resume();
}
}
}

Thread pool with bounded queue

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();
}
}
}
}

Resettable CountdownLatch

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;
}
}

Categories