[英]Resettable CountdownLatch
I need something which is directly equivalent to CountDownLatch
, but is resettable (remaining thread-safe.).我需要一些直接等同于CountDownLatch
的东西,但它是可重置的(保持线程安全)。 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.我不能使用经典的同步结构,因为它们在这种情况下根本不起作用(复杂的锁定问题),目前,我正在创建许多CountDownLatch
对象,每个对象都替换前一个。 I believe this is doing in the young generation in the GC (due to the sheer number of objects).我相信这是在 GC 的年轻一代中进行的(由于对象的绝对数量)。 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).您可以在下面看到使用锁存器的代码(它是 ns-3.network 模拟器接口的java.net
mock 的一部分)。
Some ideas might be to try CyclicBarrier
(JDK5+) or Phaser
(JDK7)一些想法可能是尝试CyclicBarrier
(JDK5+) 或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.我复制了CountDownLatch
并实现了一个reset()
方法,该方法将内部Sync
class 重置为其初始 state (起始计数):) 似乎工作正常。 No more unnecessary object creation \o/ It was not possible to subclass because sync
was private.不再有不必要的 object 创建 \o/ 因为sync
是私有的,所以无法进行子类化。 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()}
* <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
* 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() + "]";
}
}
Based on @Fidel -s answer, I made a drop-in replacement for ResettableCountDownLatch.基于@Fidel -s 的回答,我做了一个替代 ResettableCountDownLatch。 The changes I made我所做的改变
mLatch
is private volatile
mLatch
是private volatile
mInitialCount
is private final
mInitialCount
是private final
值await()
has changed to void.简单await()
的返回类型已更改为 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);
}
}
Based on @Systemplanet -s comment, here is a safer version of reset()
:基于@Systemplanet -s 评论,这里是一个更安全的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.基本上,这是简单和安全之间的选择。 Ie 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()
.即,如果您愿意将责任转移给代码的客户端,那么在reset()
中设置引用null
就足够了。
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.我不确定这是否存在致命缺陷,但我最近遇到了同样的问题,并通过在每次我想重置时简单地实例化一个新的 CountDownLatch object 来解决它。 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.显然这是一个沉重的抽象,但到目前为止它对我有用,并且不需要你修改任何 class 定义。
Phaser has more options, we can implement resettable countdownLatch using that. Phaser有更多选项,我们可以使用它来实现可重置的 countdownLatch。
Please read below basic concepts from the following sites请从以下网站阅读以下基本概念
https://examples.javacodegeeks.com/core-java/util/concurrent/phaser/java-util-concurrent-phaser-example/ 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 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();
}
}
Use Phaser.使用移相器。
if only one thread should to do work.如果只有一个线程应该工作。 U can join AtomicBoolean and Phaser你可以加入 AtomicBoolean 和 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());
}
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;
}
}
Looks like you want to turn asynchronous I/O to synchronous.看起来您想将异步 I/O 转换为同步。 The whole idea of using asynchronous I/O is to avoid threads, but CountDownLatch requres using threads.使用异步 I/O 的整个想法是避免使用线程,但 CountDownLatch 需要使用线程。 This is an obvious contradiction in your question.这在你的问题中是一个明显的矛盾。 So, you can:这样你就可以:
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.根据我从 OP 解释和源代码中能够理解的内容,可重置的CountDownLatch
对于他要解决的问题来说并不是很充分的概念。 The documentation of the CountDownLatch itself mentions the OP's use case as simple gate initialized with a count of one : CountDownLatch 本身的文档提到了 OP 的用例作为简单的门初始化,计数为 1 :
CountDownLatch
initialized with a count of one serves as a simple on/off latch, or gate: all threads invokingawait
wait at the gate until it is opened by a thread invokingcountDown
.CountDownLatch
初始化为 1,用作简单的开/关锁存器或门:所有调用await
的线程在门处等待,直到它被调用countDown
的线程打开。
, but CountDownLatch
implementation does not go any further in this direction. ,但CountDownLatch
实现不会在此方向上进一步执行 go。
So, myself having a problem similar to that of OP's I decided to introduce a SimpleGate
class with the following properties:因此,我自己遇到了类似于 OP 的问题,我决定引入具有以下属性的SimpleGate
:
Number of permits is one, which means it can be either in On
or Off
state;许可数为 1,表示可以为On
或Off
state;
There is a dedicated thread, called Gate Keeper
that is only allowed to shut off
or open up
the Gate;有一个专用线程,称为Gate Keeper
,只允许shut off
或open up
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);打开门立即允许试图come through
门的线程来做到这一点(这个非常合乎逻辑的特征在其他答案中被忽略了);
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 }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.