Java concurrency: resettable enabled/disabled wait condition
I want to let a thread sleep until a certain condition becomes off. Basically, I need three operations:
enable()
: enable sleeping mode (do nothing if already enabled)
disable()
: disable sleeping mode (do nothing if already disabled)
await()
: wait until sleeping mode becomes disabled (or return immediately if the sleeping mode was already disabled) or the thread becomes interrupted (InterruptedException
is thrown)
With this, thread A
calls enable()
. Now thread B
calls await()
and goes to sleep until thread A
(or another one) calls disable()
. This cycle can be repeated.
I know this can be quite easily done with wait()
and notify()
, but I am wondering if JDK8 has such functionality built-in?
The closest I could find is was a CountdownLatch(1)
, unfortunately the implementation is not resettable.
Basically, I just want to call enable()
/disable()
and await()
, while all concurrency concepts are abstracted in the implementation (though await()
should throw InterruptedException
, which is unavoidable).
java multithreading concurrency java.util.concurrent
add a comment |
I want to let a thread sleep until a certain condition becomes off. Basically, I need three operations:
enable()
: enable sleeping mode (do nothing if already enabled)
disable()
: disable sleeping mode (do nothing if already disabled)
await()
: wait until sleeping mode becomes disabled (or return immediately if the sleeping mode was already disabled) or the thread becomes interrupted (InterruptedException
is thrown)
With this, thread A
calls enable()
. Now thread B
calls await()
and goes to sleep until thread A
(or another one) calls disable()
. This cycle can be repeated.
I know this can be quite easily done with wait()
and notify()
, but I am wondering if JDK8 has such functionality built-in?
The closest I could find is was a CountdownLatch(1)
, unfortunately the implementation is not resettable.
Basically, I just want to call enable()
/disable()
and await()
, while all concurrency concepts are abstracted in the implementation (though await()
should throw InterruptedException
, which is unavoidable).
java multithreading concurrency java.util.concurrent
Lock
should work, egReentrantLock
.
– daniu
Nov 22 '18 at 10:20
@daniu WithLock
, I believe I still have to model the wait condition myself. All aLock
does is providing a critical section to manipulate that wait condition and inform waiting parties safely, as far as I know. Basically, I just want to callenable()
/disable()
andawait()
, while all concurrency concepts are abstracted in the implementation (thoughawait()
should throwInterruptedException
, which is unavoidable).
– OneLastSpark
Nov 22 '18 at 10:24
add a comment |
I want to let a thread sleep until a certain condition becomes off. Basically, I need three operations:
enable()
: enable sleeping mode (do nothing if already enabled)
disable()
: disable sleeping mode (do nothing if already disabled)
await()
: wait until sleeping mode becomes disabled (or return immediately if the sleeping mode was already disabled) or the thread becomes interrupted (InterruptedException
is thrown)
With this, thread A
calls enable()
. Now thread B
calls await()
and goes to sleep until thread A
(or another one) calls disable()
. This cycle can be repeated.
I know this can be quite easily done with wait()
and notify()
, but I am wondering if JDK8 has such functionality built-in?
The closest I could find is was a CountdownLatch(1)
, unfortunately the implementation is not resettable.
Basically, I just want to call enable()
/disable()
and await()
, while all concurrency concepts are abstracted in the implementation (though await()
should throw InterruptedException
, which is unavoidable).
java multithreading concurrency java.util.concurrent
I want to let a thread sleep until a certain condition becomes off. Basically, I need three operations:
enable()
: enable sleeping mode (do nothing if already enabled)
disable()
: disable sleeping mode (do nothing if already disabled)
await()
: wait until sleeping mode becomes disabled (or return immediately if the sleeping mode was already disabled) or the thread becomes interrupted (InterruptedException
is thrown)
With this, thread A
calls enable()
. Now thread B
calls await()
and goes to sleep until thread A
(or another one) calls disable()
. This cycle can be repeated.
I know this can be quite easily done with wait()
and notify()
, but I am wondering if JDK8 has such functionality built-in?
The closest I could find is was a CountdownLatch(1)
, unfortunately the implementation is not resettable.
Basically, I just want to call enable()
/disable()
and await()
, while all concurrency concepts are abstracted in the implementation (though await()
should throw InterruptedException
, which is unavoidable).
java multithreading concurrency java.util.concurrent
java multithreading concurrency java.util.concurrent
edited Nov 22 '18 at 10:39
OneLastSpark
asked Nov 22 '18 at 10:13
OneLastSparkOneLastSpark
386
386
Lock
should work, egReentrantLock
.
– daniu
Nov 22 '18 at 10:20
@daniu WithLock
, I believe I still have to model the wait condition myself. All aLock
does is providing a critical section to manipulate that wait condition and inform waiting parties safely, as far as I know. Basically, I just want to callenable()
/disable()
andawait()
, while all concurrency concepts are abstracted in the implementation (thoughawait()
should throwInterruptedException
, which is unavoidable).
– OneLastSpark
Nov 22 '18 at 10:24
add a comment |
Lock
should work, egReentrantLock
.
– daniu
Nov 22 '18 at 10:20
@daniu WithLock
, I believe I still have to model the wait condition myself. All aLock
does is providing a critical section to manipulate that wait condition and inform waiting parties safely, as far as I know. Basically, I just want to callenable()
/disable()
andawait()
, while all concurrency concepts are abstracted in the implementation (thoughawait()
should throwInterruptedException
, which is unavoidable).
– OneLastSpark
Nov 22 '18 at 10:24
Lock
should work, eg ReentrantLock
.– daniu
Nov 22 '18 at 10:20
Lock
should work, eg ReentrantLock
.– daniu
Nov 22 '18 at 10:20
@daniu With
Lock
, I believe I still have to model the wait condition myself. All a Lock
does is providing a critical section to manipulate that wait condition and inform waiting parties safely, as far as I know. Basically, I just want to call enable()
/disable()
and await()
, while all concurrency concepts are abstracted in the implementation (though await()
should throw InterruptedException
, which is unavoidable).– OneLastSpark
Nov 22 '18 at 10:24
@daniu With
Lock
, I believe I still have to model the wait condition myself. All a Lock
does is providing a critical section to manipulate that wait condition and inform waiting parties safely, as far as I know. Basically, I just want to call enable()
/disable()
and await()
, while all concurrency concepts are abstracted in the implementation (though await()
should throw InterruptedException
, which is unavoidable).– OneLastSpark
Nov 22 '18 at 10:24
add a comment |
4 Answers
4
active
oldest
votes
Another possible implementation of Switch
:
public class Switch {
private final AtomicBoolean state = new AtomicBoolean();
public void enable() {
state.set(true);
}
public void disable() {
if (state.compareAndSet(true, false)) {
synchronized (state) {
state.notifyAll();
}
}
}
public void await() throws InterruptedException {
if (state.get()) {
synchronized (state) {
while (state.get()) {
state.wait();
}
}
}
}
}
add a comment |
You could use a Semaphor
too :
import java.util.concurrent.Semaphore;
public class Switch {
private Semaphore semaphore = new Semaphore(1);
public void enable() {
synchronized(this) {
semaphore.drainPermits(); // 0
semaphore.reducePermits(1); // -1 or 0
}
}
public void disable() {
semaphore.release(2); // 1 or 2
}
public void await() throws InterruptedException {
semaphore.acquire();
semaphore.release();
}
}
This comes closest to that I wanted. I was hoping suchSwitch
implementation already existed in theconcurrent
package.
– OneLastSpark
Nov 23 '18 at 18:03
As an afterthought, this solution is not correct. There is a small chance thatenable()
will be called between the two operations inawait()
. In that case,enable()
will be ignored. I think it can be solved by adding a spin wait loop inenable()
:while(semaphore.drainPermits() == 0) { Thread.yield(); }
.
– OneLastSpark
Nov 26 '18 at 19:32
In JDK9, we can doThread.onSpinWait()
instead ofThread.yield()
.
– OneLastSpark
Nov 26 '18 at 19:33
indeed. The issue withThread.yield
is that if you callenable()
while the sleep mode is already enabled, the method won't return. I think we can avoid the issue by incrementing the number of permits for enable / disable action to 2. (see update) (but we need to prevent 2 simultaneous calls toenable
to avoid going down to -2)
– Thierry
Nov 26 '18 at 23:01
add a comment |
You could use Condition
:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Switch {
private final Lock lock = new ReentrantLock();
private final Condition on = lock.newCondition();
private final Condition off = lock.newCondition();
private volatile boolean state = true;
public void enable() {
try {
lock.lock();
state = true;
on.signalAll();
} finally {
lock.unlock();
}
}
public void disable() {
try {
lock.lock();
state = false;
off.signalAll();
} finally {
lock.unlock();
}
}
public void await() {
try {
lock.lock();
while(!state) {
try {
off.await();
} catch (InterruptedException e) {
throw new RuntimeException("waiting interrupted.");
}
}
} finally {
lock.unlock();
}
}
}
I would upvote this as well, but this has a few problems. First, it's better to letawait()
just throw theInterruptedException
. There is no need to translate to aRuntimeException
. If you still decide to do that however, you have to reset the interrupted flag before throwing theRuntimeException
. Second, you are signaling theon
lock, but nobody is ever waiting on that. You can remove that. Third, there is no need to makestate
volatile, as it is already guarded by the memory barrier set by the lock.
– OneLastSpark
Nov 23 '18 at 18:06
Also, another downside of this implementation is that for everyawait()
call, a lock must be acquired. Your other answer (Semaphore
based) and my answer do not suffer from that. Both have some kind of fail-fast path, which relies onvolatile
visibility. This is usually a bit faster than acquiring a lock each time.
– OneLastSpark
Nov 23 '18 at 18:46
add a comment |
enable(): enable sleeping mode (do nothing if already enabled)
disable(): disable sleeping mode (do nothing if already disabled)
do nothing if already enabled (disabled)
is a bad design, which can lead to subtle bugs which are hard to reproduce and discover. For example, let sleeping mode is disabled, and one thread calls disable()
and the other calls enable()
. Depending on which call is made first, the mode will stay enabled or disabled forever. To make execution more deterministic, enabling and disabling must be counted, and the final state will be determined (disabled).
Instead, your threads should exchange tokens which do not mask each other. Besides CountdownLatch
, (which effectively is a counter of prohibitions), JDK has CyclicBarrier
and Phaser
, which are resettable counters of prohibitions, and Semaphore
, which is a counter of permissions.
UPDT
this implementation may work (I did not tested it):
Phaser p = new Phaser(1);
public void await() {
p.arriveAndAwaitAdvance();
}
public void enable() {
p.register();
}
public void disable() {
p.arriveAndDeregister();
}
N sequential calls to enable()
require the same number of disable()
to pass the awaiting thread.
Could you show how you would implement what @OneLastSpark asks with one of the three classes you mention ? (CyclicBarrier
,Phaser
orSemaphor
) ? I tried to come up to something with one of these, but there was always a use case that would not work with these (in particular during the switch off of the sleeping mode when the thread B was not waiting already)
– Thierry
Nov 22 '18 at 15:44
1
In the question : until thread A (or another one) callsdisable()
. ButarriveAndDeregister()
(disable()
) must be called by the thread that calledregister()
(enable()
)
– Thierry
Nov 23 '18 at 13:40
1
Also, if two different threads calls theenable()
method both will have to call thedisable()
method for the await to end. Which implies that calling theenable()
method should be done in atry
and thedisable()
method in thefinally
block otherwise the switch will be blocked longer than intended (potentially until jvm stops).
– Thierry
Nov 23 '18 at 13:58
@Thierry looks like you are right.
– Alexei Kaigorodov
Nov 23 '18 at 14:26
I understand you concerns and remark regarding passing a token, but this is not really applicable to my question. I have no token to pass, and I never ever wantenable()
ordisable()
to block. Thierry's concerns are spot on.
– OneLastSpark
Nov 23 '18 at 18:14
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53428589%2fjava-concurrency-resettable-enabled-disabled-wait-condition%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
Another possible implementation of Switch
:
public class Switch {
private final AtomicBoolean state = new AtomicBoolean();
public void enable() {
state.set(true);
}
public void disable() {
if (state.compareAndSet(true, false)) {
synchronized (state) {
state.notifyAll();
}
}
}
public void await() throws InterruptedException {
if (state.get()) {
synchronized (state) {
while (state.get()) {
state.wait();
}
}
}
}
}
add a comment |
Another possible implementation of Switch
:
public class Switch {
private final AtomicBoolean state = new AtomicBoolean();
public void enable() {
state.set(true);
}
public void disable() {
if (state.compareAndSet(true, false)) {
synchronized (state) {
state.notifyAll();
}
}
}
public void await() throws InterruptedException {
if (state.get()) {
synchronized (state) {
while (state.get()) {
state.wait();
}
}
}
}
}
add a comment |
Another possible implementation of Switch
:
public class Switch {
private final AtomicBoolean state = new AtomicBoolean();
public void enable() {
state.set(true);
}
public void disable() {
if (state.compareAndSet(true, false)) {
synchronized (state) {
state.notifyAll();
}
}
}
public void await() throws InterruptedException {
if (state.get()) {
synchronized (state) {
while (state.get()) {
state.wait();
}
}
}
}
}
Another possible implementation of Switch
:
public class Switch {
private final AtomicBoolean state = new AtomicBoolean();
public void enable() {
state.set(true);
}
public void disable() {
if (state.compareAndSet(true, false)) {
synchronized (state) {
state.notifyAll();
}
}
}
public void await() throws InterruptedException {
if (state.get()) {
synchronized (state) {
while (state.get()) {
state.wait();
}
}
}
}
}
answered Nov 23 '18 at 18:30
OneLastSparkOneLastSpark
386
386
add a comment |
add a comment |
You could use a Semaphor
too :
import java.util.concurrent.Semaphore;
public class Switch {
private Semaphore semaphore = new Semaphore(1);
public void enable() {
synchronized(this) {
semaphore.drainPermits(); // 0
semaphore.reducePermits(1); // -1 or 0
}
}
public void disable() {
semaphore.release(2); // 1 or 2
}
public void await() throws InterruptedException {
semaphore.acquire();
semaphore.release();
}
}
This comes closest to that I wanted. I was hoping suchSwitch
implementation already existed in theconcurrent
package.
– OneLastSpark
Nov 23 '18 at 18:03
As an afterthought, this solution is not correct. There is a small chance thatenable()
will be called between the two operations inawait()
. In that case,enable()
will be ignored. I think it can be solved by adding a spin wait loop inenable()
:while(semaphore.drainPermits() == 0) { Thread.yield(); }
.
– OneLastSpark
Nov 26 '18 at 19:32
In JDK9, we can doThread.onSpinWait()
instead ofThread.yield()
.
– OneLastSpark
Nov 26 '18 at 19:33
indeed. The issue withThread.yield
is that if you callenable()
while the sleep mode is already enabled, the method won't return. I think we can avoid the issue by incrementing the number of permits for enable / disable action to 2. (see update) (but we need to prevent 2 simultaneous calls toenable
to avoid going down to -2)
– Thierry
Nov 26 '18 at 23:01
add a comment |
You could use a Semaphor
too :
import java.util.concurrent.Semaphore;
public class Switch {
private Semaphore semaphore = new Semaphore(1);
public void enable() {
synchronized(this) {
semaphore.drainPermits(); // 0
semaphore.reducePermits(1); // -1 or 0
}
}
public void disable() {
semaphore.release(2); // 1 or 2
}
public void await() throws InterruptedException {
semaphore.acquire();
semaphore.release();
}
}
This comes closest to that I wanted. I was hoping suchSwitch
implementation already existed in theconcurrent
package.
– OneLastSpark
Nov 23 '18 at 18:03
As an afterthought, this solution is not correct. There is a small chance thatenable()
will be called between the two operations inawait()
. In that case,enable()
will be ignored. I think it can be solved by adding a spin wait loop inenable()
:while(semaphore.drainPermits() == 0) { Thread.yield(); }
.
– OneLastSpark
Nov 26 '18 at 19:32
In JDK9, we can doThread.onSpinWait()
instead ofThread.yield()
.
– OneLastSpark
Nov 26 '18 at 19:33
indeed. The issue withThread.yield
is that if you callenable()
while the sleep mode is already enabled, the method won't return. I think we can avoid the issue by incrementing the number of permits for enable / disable action to 2. (see update) (but we need to prevent 2 simultaneous calls toenable
to avoid going down to -2)
– Thierry
Nov 26 '18 at 23:01
add a comment |
You could use a Semaphor
too :
import java.util.concurrent.Semaphore;
public class Switch {
private Semaphore semaphore = new Semaphore(1);
public void enable() {
synchronized(this) {
semaphore.drainPermits(); // 0
semaphore.reducePermits(1); // -1 or 0
}
}
public void disable() {
semaphore.release(2); // 1 or 2
}
public void await() throws InterruptedException {
semaphore.acquire();
semaphore.release();
}
}
You could use a Semaphor
too :
import java.util.concurrent.Semaphore;
public class Switch {
private Semaphore semaphore = new Semaphore(1);
public void enable() {
synchronized(this) {
semaphore.drainPermits(); // 0
semaphore.reducePermits(1); // -1 or 0
}
}
public void disable() {
semaphore.release(2); // 1 or 2
}
public void await() throws InterruptedException {
semaphore.acquire();
semaphore.release();
}
}
edited Nov 26 '18 at 23:03
answered Nov 23 '18 at 14:27
ThierryThierry
3,6822129
3,6822129
This comes closest to that I wanted. I was hoping suchSwitch
implementation already existed in theconcurrent
package.
– OneLastSpark
Nov 23 '18 at 18:03
As an afterthought, this solution is not correct. There is a small chance thatenable()
will be called between the two operations inawait()
. In that case,enable()
will be ignored. I think it can be solved by adding a spin wait loop inenable()
:while(semaphore.drainPermits() == 0) { Thread.yield(); }
.
– OneLastSpark
Nov 26 '18 at 19:32
In JDK9, we can doThread.onSpinWait()
instead ofThread.yield()
.
– OneLastSpark
Nov 26 '18 at 19:33
indeed. The issue withThread.yield
is that if you callenable()
while the sleep mode is already enabled, the method won't return. I think we can avoid the issue by incrementing the number of permits for enable / disable action to 2. (see update) (but we need to prevent 2 simultaneous calls toenable
to avoid going down to -2)
– Thierry
Nov 26 '18 at 23:01
add a comment |
This comes closest to that I wanted. I was hoping suchSwitch
implementation already existed in theconcurrent
package.
– OneLastSpark
Nov 23 '18 at 18:03
As an afterthought, this solution is not correct. There is a small chance thatenable()
will be called between the two operations inawait()
. In that case,enable()
will be ignored. I think it can be solved by adding a spin wait loop inenable()
:while(semaphore.drainPermits() == 0) { Thread.yield(); }
.
– OneLastSpark
Nov 26 '18 at 19:32
In JDK9, we can doThread.onSpinWait()
instead ofThread.yield()
.
– OneLastSpark
Nov 26 '18 at 19:33
indeed. The issue withThread.yield
is that if you callenable()
while the sleep mode is already enabled, the method won't return. I think we can avoid the issue by incrementing the number of permits for enable / disable action to 2. (see update) (but we need to prevent 2 simultaneous calls toenable
to avoid going down to -2)
– Thierry
Nov 26 '18 at 23:01
This comes closest to that I wanted. I was hoping such
Switch
implementation already existed in the concurrent
package.– OneLastSpark
Nov 23 '18 at 18:03
This comes closest to that I wanted. I was hoping such
Switch
implementation already existed in the concurrent
package.– OneLastSpark
Nov 23 '18 at 18:03
As an afterthought, this solution is not correct. There is a small chance that
enable()
will be called between the two operations in await()
. In that case, enable()
will be ignored. I think it can be solved by adding a spin wait loop in enable()
: while(semaphore.drainPermits() == 0) { Thread.yield(); }
.– OneLastSpark
Nov 26 '18 at 19:32
As an afterthought, this solution is not correct. There is a small chance that
enable()
will be called between the two operations in await()
. In that case, enable()
will be ignored. I think it can be solved by adding a spin wait loop in enable()
: while(semaphore.drainPermits() == 0) { Thread.yield(); }
.– OneLastSpark
Nov 26 '18 at 19:32
In JDK9, we can do
Thread.onSpinWait()
instead of Thread.yield()
.– OneLastSpark
Nov 26 '18 at 19:33
In JDK9, we can do
Thread.onSpinWait()
instead of Thread.yield()
.– OneLastSpark
Nov 26 '18 at 19:33
indeed. The issue with
Thread.yield
is that if you call enable()
while the sleep mode is already enabled, the method won't return. I think we can avoid the issue by incrementing the number of permits for enable / disable action to 2. (see update) (but we need to prevent 2 simultaneous calls to enable
to avoid going down to -2)– Thierry
Nov 26 '18 at 23:01
indeed. The issue with
Thread.yield
is that if you call enable()
while the sleep mode is already enabled, the method won't return. I think we can avoid the issue by incrementing the number of permits for enable / disable action to 2. (see update) (but we need to prevent 2 simultaneous calls to enable
to avoid going down to -2)– Thierry
Nov 26 '18 at 23:01
add a comment |
You could use Condition
:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Switch {
private final Lock lock = new ReentrantLock();
private final Condition on = lock.newCondition();
private final Condition off = lock.newCondition();
private volatile boolean state = true;
public void enable() {
try {
lock.lock();
state = true;
on.signalAll();
} finally {
lock.unlock();
}
}
public void disable() {
try {
lock.lock();
state = false;
off.signalAll();
} finally {
lock.unlock();
}
}
public void await() {
try {
lock.lock();
while(!state) {
try {
off.await();
} catch (InterruptedException e) {
throw new RuntimeException("waiting interrupted.");
}
}
} finally {
lock.unlock();
}
}
}
I would upvote this as well, but this has a few problems. First, it's better to letawait()
just throw theInterruptedException
. There is no need to translate to aRuntimeException
. If you still decide to do that however, you have to reset the interrupted flag before throwing theRuntimeException
. Second, you are signaling theon
lock, but nobody is ever waiting on that. You can remove that. Third, there is no need to makestate
volatile, as it is already guarded by the memory barrier set by the lock.
– OneLastSpark
Nov 23 '18 at 18:06
Also, another downside of this implementation is that for everyawait()
call, a lock must be acquired. Your other answer (Semaphore
based) and my answer do not suffer from that. Both have some kind of fail-fast path, which relies onvolatile
visibility. This is usually a bit faster than acquiring a lock each time.
– OneLastSpark
Nov 23 '18 at 18:46
add a comment |
You could use Condition
:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Switch {
private final Lock lock = new ReentrantLock();
private final Condition on = lock.newCondition();
private final Condition off = lock.newCondition();
private volatile boolean state = true;
public void enable() {
try {
lock.lock();
state = true;
on.signalAll();
} finally {
lock.unlock();
}
}
public void disable() {
try {
lock.lock();
state = false;
off.signalAll();
} finally {
lock.unlock();
}
}
public void await() {
try {
lock.lock();
while(!state) {
try {
off.await();
} catch (InterruptedException e) {
throw new RuntimeException("waiting interrupted.");
}
}
} finally {
lock.unlock();
}
}
}
I would upvote this as well, but this has a few problems. First, it's better to letawait()
just throw theInterruptedException
. There is no need to translate to aRuntimeException
. If you still decide to do that however, you have to reset the interrupted flag before throwing theRuntimeException
. Second, you are signaling theon
lock, but nobody is ever waiting on that. You can remove that. Third, there is no need to makestate
volatile, as it is already guarded by the memory barrier set by the lock.
– OneLastSpark
Nov 23 '18 at 18:06
Also, another downside of this implementation is that for everyawait()
call, a lock must be acquired. Your other answer (Semaphore
based) and my answer do not suffer from that. Both have some kind of fail-fast path, which relies onvolatile
visibility. This is usually a bit faster than acquiring a lock each time.
– OneLastSpark
Nov 23 '18 at 18:46
add a comment |
You could use Condition
:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Switch {
private final Lock lock = new ReentrantLock();
private final Condition on = lock.newCondition();
private final Condition off = lock.newCondition();
private volatile boolean state = true;
public void enable() {
try {
lock.lock();
state = true;
on.signalAll();
} finally {
lock.unlock();
}
}
public void disable() {
try {
lock.lock();
state = false;
off.signalAll();
} finally {
lock.unlock();
}
}
public void await() {
try {
lock.lock();
while(!state) {
try {
off.await();
} catch (InterruptedException e) {
throw new RuntimeException("waiting interrupted.");
}
}
} finally {
lock.unlock();
}
}
}
You could use Condition
:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Switch {
private final Lock lock = new ReentrantLock();
private final Condition on = lock.newCondition();
private final Condition off = lock.newCondition();
private volatile boolean state = true;
public void enable() {
try {
lock.lock();
state = true;
on.signalAll();
} finally {
lock.unlock();
}
}
public void disable() {
try {
lock.lock();
state = false;
off.signalAll();
} finally {
lock.unlock();
}
}
public void await() {
try {
lock.lock();
while(!state) {
try {
off.await();
} catch (InterruptedException e) {
throw new RuntimeException("waiting interrupted.");
}
}
} finally {
lock.unlock();
}
}
}
answered Nov 22 '18 at 10:57
ThierryThierry
3,6822129
3,6822129
I would upvote this as well, but this has a few problems. First, it's better to letawait()
just throw theInterruptedException
. There is no need to translate to aRuntimeException
. If you still decide to do that however, you have to reset the interrupted flag before throwing theRuntimeException
. Second, you are signaling theon
lock, but nobody is ever waiting on that. You can remove that. Third, there is no need to makestate
volatile, as it is already guarded by the memory barrier set by the lock.
– OneLastSpark
Nov 23 '18 at 18:06
Also, another downside of this implementation is that for everyawait()
call, a lock must be acquired. Your other answer (Semaphore
based) and my answer do not suffer from that. Both have some kind of fail-fast path, which relies onvolatile
visibility. This is usually a bit faster than acquiring a lock each time.
– OneLastSpark
Nov 23 '18 at 18:46
add a comment |
I would upvote this as well, but this has a few problems. First, it's better to letawait()
just throw theInterruptedException
. There is no need to translate to aRuntimeException
. If you still decide to do that however, you have to reset the interrupted flag before throwing theRuntimeException
. Second, you are signaling theon
lock, but nobody is ever waiting on that. You can remove that. Third, there is no need to makestate
volatile, as it is already guarded by the memory barrier set by the lock.
– OneLastSpark
Nov 23 '18 at 18:06
Also, another downside of this implementation is that for everyawait()
call, a lock must be acquired. Your other answer (Semaphore
based) and my answer do not suffer from that. Both have some kind of fail-fast path, which relies onvolatile
visibility. This is usually a bit faster than acquiring a lock each time.
– OneLastSpark
Nov 23 '18 at 18:46
I would upvote this as well, but this has a few problems. First, it's better to let
await()
just throw the InterruptedException
. There is no need to translate to a RuntimeException
. If you still decide to do that however, you have to reset the interrupted flag before throwing the RuntimeException
. Second, you are signaling the on
lock, but nobody is ever waiting on that. You can remove that. Third, there is no need to make state
volatile, as it is already guarded by the memory barrier set by the lock.– OneLastSpark
Nov 23 '18 at 18:06
I would upvote this as well, but this has a few problems. First, it's better to let
await()
just throw the InterruptedException
. There is no need to translate to a RuntimeException
. If you still decide to do that however, you have to reset the interrupted flag before throwing the RuntimeException
. Second, you are signaling the on
lock, but nobody is ever waiting on that. You can remove that. Third, there is no need to make state
volatile, as it is already guarded by the memory barrier set by the lock.– OneLastSpark
Nov 23 '18 at 18:06
Also, another downside of this implementation is that for every
await()
call, a lock must be acquired. Your other answer (Semaphore
based) and my answer do not suffer from that. Both have some kind of fail-fast path, which relies on volatile
visibility. This is usually a bit faster than acquiring a lock each time.– OneLastSpark
Nov 23 '18 at 18:46
Also, another downside of this implementation is that for every
await()
call, a lock must be acquired. Your other answer (Semaphore
based) and my answer do not suffer from that. Both have some kind of fail-fast path, which relies on volatile
visibility. This is usually a bit faster than acquiring a lock each time.– OneLastSpark
Nov 23 '18 at 18:46
add a comment |
enable(): enable sleeping mode (do nothing if already enabled)
disable(): disable sleeping mode (do nothing if already disabled)
do nothing if already enabled (disabled)
is a bad design, which can lead to subtle bugs which are hard to reproduce and discover. For example, let sleeping mode is disabled, and one thread calls disable()
and the other calls enable()
. Depending on which call is made first, the mode will stay enabled or disabled forever. To make execution more deterministic, enabling and disabling must be counted, and the final state will be determined (disabled).
Instead, your threads should exchange tokens which do not mask each other. Besides CountdownLatch
, (which effectively is a counter of prohibitions), JDK has CyclicBarrier
and Phaser
, which are resettable counters of prohibitions, and Semaphore
, which is a counter of permissions.
UPDT
this implementation may work (I did not tested it):
Phaser p = new Phaser(1);
public void await() {
p.arriveAndAwaitAdvance();
}
public void enable() {
p.register();
}
public void disable() {
p.arriveAndDeregister();
}
N sequential calls to enable()
require the same number of disable()
to pass the awaiting thread.
Could you show how you would implement what @OneLastSpark asks with one of the three classes you mention ? (CyclicBarrier
,Phaser
orSemaphor
) ? I tried to come up to something with one of these, but there was always a use case that would not work with these (in particular during the switch off of the sleeping mode when the thread B was not waiting already)
– Thierry
Nov 22 '18 at 15:44
1
In the question : until thread A (or another one) callsdisable()
. ButarriveAndDeregister()
(disable()
) must be called by the thread that calledregister()
(enable()
)
– Thierry
Nov 23 '18 at 13:40
1
Also, if two different threads calls theenable()
method both will have to call thedisable()
method for the await to end. Which implies that calling theenable()
method should be done in atry
and thedisable()
method in thefinally
block otherwise the switch will be blocked longer than intended (potentially until jvm stops).
– Thierry
Nov 23 '18 at 13:58
@Thierry looks like you are right.
– Alexei Kaigorodov
Nov 23 '18 at 14:26
I understand you concerns and remark regarding passing a token, but this is not really applicable to my question. I have no token to pass, and I never ever wantenable()
ordisable()
to block. Thierry's concerns are spot on.
– OneLastSpark
Nov 23 '18 at 18:14
add a comment |
enable(): enable sleeping mode (do nothing if already enabled)
disable(): disable sleeping mode (do nothing if already disabled)
do nothing if already enabled (disabled)
is a bad design, which can lead to subtle bugs which are hard to reproduce and discover. For example, let sleeping mode is disabled, and one thread calls disable()
and the other calls enable()
. Depending on which call is made first, the mode will stay enabled or disabled forever. To make execution more deterministic, enabling and disabling must be counted, and the final state will be determined (disabled).
Instead, your threads should exchange tokens which do not mask each other. Besides CountdownLatch
, (which effectively is a counter of prohibitions), JDK has CyclicBarrier
and Phaser
, which are resettable counters of prohibitions, and Semaphore
, which is a counter of permissions.
UPDT
this implementation may work (I did not tested it):
Phaser p = new Phaser(1);
public void await() {
p.arriveAndAwaitAdvance();
}
public void enable() {
p.register();
}
public void disable() {
p.arriveAndDeregister();
}
N sequential calls to enable()
require the same number of disable()
to pass the awaiting thread.
Could you show how you would implement what @OneLastSpark asks with one of the three classes you mention ? (CyclicBarrier
,Phaser
orSemaphor
) ? I tried to come up to something with one of these, but there was always a use case that would not work with these (in particular during the switch off of the sleeping mode when the thread B was not waiting already)
– Thierry
Nov 22 '18 at 15:44
1
In the question : until thread A (or another one) callsdisable()
. ButarriveAndDeregister()
(disable()
) must be called by the thread that calledregister()
(enable()
)
– Thierry
Nov 23 '18 at 13:40
1
Also, if two different threads calls theenable()
method both will have to call thedisable()
method for the await to end. Which implies that calling theenable()
method should be done in atry
and thedisable()
method in thefinally
block otherwise the switch will be blocked longer than intended (potentially until jvm stops).
– Thierry
Nov 23 '18 at 13:58
@Thierry looks like you are right.
– Alexei Kaigorodov
Nov 23 '18 at 14:26
I understand you concerns and remark regarding passing a token, but this is not really applicable to my question. I have no token to pass, and I never ever wantenable()
ordisable()
to block. Thierry's concerns are spot on.
– OneLastSpark
Nov 23 '18 at 18:14
add a comment |
enable(): enable sleeping mode (do nothing if already enabled)
disable(): disable sleeping mode (do nothing if already disabled)
do nothing if already enabled (disabled)
is a bad design, which can lead to subtle bugs which are hard to reproduce and discover. For example, let sleeping mode is disabled, and one thread calls disable()
and the other calls enable()
. Depending on which call is made first, the mode will stay enabled or disabled forever. To make execution more deterministic, enabling and disabling must be counted, and the final state will be determined (disabled).
Instead, your threads should exchange tokens which do not mask each other. Besides CountdownLatch
, (which effectively is a counter of prohibitions), JDK has CyclicBarrier
and Phaser
, which are resettable counters of prohibitions, and Semaphore
, which is a counter of permissions.
UPDT
this implementation may work (I did not tested it):
Phaser p = new Phaser(1);
public void await() {
p.arriveAndAwaitAdvance();
}
public void enable() {
p.register();
}
public void disable() {
p.arriveAndDeregister();
}
N sequential calls to enable()
require the same number of disable()
to pass the awaiting thread.
enable(): enable sleeping mode (do nothing if already enabled)
disable(): disable sleeping mode (do nothing if already disabled)
do nothing if already enabled (disabled)
is a bad design, which can lead to subtle bugs which are hard to reproduce and discover. For example, let sleeping mode is disabled, and one thread calls disable()
and the other calls enable()
. Depending on which call is made first, the mode will stay enabled or disabled forever. To make execution more deterministic, enabling and disabling must be counted, and the final state will be determined (disabled).
Instead, your threads should exchange tokens which do not mask each other. Besides CountdownLatch
, (which effectively is a counter of prohibitions), JDK has CyclicBarrier
and Phaser
, which are resettable counters of prohibitions, and Semaphore
, which is a counter of permissions.
UPDT
this implementation may work (I did not tested it):
Phaser p = new Phaser(1);
public void await() {
p.arriveAndAwaitAdvance();
}
public void enable() {
p.register();
}
public void disable() {
p.arriveAndDeregister();
}
N sequential calls to enable()
require the same number of disable()
to pass the awaiting thread.
edited Nov 23 '18 at 5:04
answered Nov 22 '18 at 15:39
Alexei KaigorodovAlexei Kaigorodov
10.1k11129
10.1k11129
Could you show how you would implement what @OneLastSpark asks with one of the three classes you mention ? (CyclicBarrier
,Phaser
orSemaphor
) ? I tried to come up to something with one of these, but there was always a use case that would not work with these (in particular during the switch off of the sleeping mode when the thread B was not waiting already)
– Thierry
Nov 22 '18 at 15:44
1
In the question : until thread A (or another one) callsdisable()
. ButarriveAndDeregister()
(disable()
) must be called by the thread that calledregister()
(enable()
)
– Thierry
Nov 23 '18 at 13:40
1
Also, if two different threads calls theenable()
method both will have to call thedisable()
method for the await to end. Which implies that calling theenable()
method should be done in atry
and thedisable()
method in thefinally
block otherwise the switch will be blocked longer than intended (potentially until jvm stops).
– Thierry
Nov 23 '18 at 13:58
@Thierry looks like you are right.
– Alexei Kaigorodov
Nov 23 '18 at 14:26
I understand you concerns and remark regarding passing a token, but this is not really applicable to my question. I have no token to pass, and I never ever wantenable()
ordisable()
to block. Thierry's concerns are spot on.
– OneLastSpark
Nov 23 '18 at 18:14
add a comment |
Could you show how you would implement what @OneLastSpark asks with one of the three classes you mention ? (CyclicBarrier
,Phaser
orSemaphor
) ? I tried to come up to something with one of these, but there was always a use case that would not work with these (in particular during the switch off of the sleeping mode when the thread B was not waiting already)
– Thierry
Nov 22 '18 at 15:44
1
In the question : until thread A (or another one) callsdisable()
. ButarriveAndDeregister()
(disable()
) must be called by the thread that calledregister()
(enable()
)
– Thierry
Nov 23 '18 at 13:40
1
Also, if two different threads calls theenable()
method both will have to call thedisable()
method for the await to end. Which implies that calling theenable()
method should be done in atry
and thedisable()
method in thefinally
block otherwise the switch will be blocked longer than intended (potentially until jvm stops).
– Thierry
Nov 23 '18 at 13:58
@Thierry looks like you are right.
– Alexei Kaigorodov
Nov 23 '18 at 14:26
I understand you concerns and remark regarding passing a token, but this is not really applicable to my question. I have no token to pass, and I never ever wantenable()
ordisable()
to block. Thierry's concerns are spot on.
– OneLastSpark
Nov 23 '18 at 18:14
Could you show how you would implement what @OneLastSpark asks with one of the three classes you mention ? (
CyclicBarrier
, Phaser
or Semaphor
) ? I tried to come up to something with one of these, but there was always a use case that would not work with these (in particular during the switch off of the sleeping mode when the thread B was not waiting already)– Thierry
Nov 22 '18 at 15:44
Could you show how you would implement what @OneLastSpark asks with one of the three classes you mention ? (
CyclicBarrier
, Phaser
or Semaphor
) ? I tried to come up to something with one of these, but there was always a use case that would not work with these (in particular during the switch off of the sleeping mode when the thread B was not waiting already)– Thierry
Nov 22 '18 at 15:44
1
1
In the question : until thread A (or another one) calls
disable()
. But arriveAndDeregister()
(disable()
) must be called by the thread that called register()
(enable()
)– Thierry
Nov 23 '18 at 13:40
In the question : until thread A (or another one) calls
disable()
. But arriveAndDeregister()
(disable()
) must be called by the thread that called register()
(enable()
)– Thierry
Nov 23 '18 at 13:40
1
1
Also, if two different threads calls the
enable()
method both will have to call the disable()
method for the await to end. Which implies that calling the enable()
method should be done in a try
and the disable()
method in the finally
block otherwise the switch will be blocked longer than intended (potentially until jvm stops).– Thierry
Nov 23 '18 at 13:58
Also, if two different threads calls the
enable()
method both will have to call the disable()
method for the await to end. Which implies that calling the enable()
method should be done in a try
and the disable()
method in the finally
block otherwise the switch will be blocked longer than intended (potentially until jvm stops).– Thierry
Nov 23 '18 at 13:58
@Thierry looks like you are right.
– Alexei Kaigorodov
Nov 23 '18 at 14:26
@Thierry looks like you are right.
– Alexei Kaigorodov
Nov 23 '18 at 14:26
I understand you concerns and remark regarding passing a token, but this is not really applicable to my question. I have no token to pass, and I never ever want
enable()
or disable()
to block. Thierry's concerns are spot on.– OneLastSpark
Nov 23 '18 at 18:14
I understand you concerns and remark regarding passing a token, but this is not really applicable to my question. I have no token to pass, and I never ever want
enable()
or disable()
to block. Thierry's concerns are spot on.– OneLastSpark
Nov 23 '18 at 18:14
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53428589%2fjava-concurrency-resettable-enabled-disabled-wait-condition%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Lock
should work, egReentrantLock
.– daniu
Nov 22 '18 at 10:20
@daniu With
Lock
, I believe I still have to model the wait condition myself. All aLock
does is providing a critical section to manipulate that wait condition and inform waiting parties safely, as far as I know. Basically, I just want to callenable()
/disable()
andawait()
, while all concurrency concepts are abstracted in the implementation (thoughawait()
should throwInterruptedException
, which is unavoidable).– OneLastSpark
Nov 22 '18 at 10:24