1
1
package com .github .akurilov .concurrent ;
2
2
3
3
import java .io .IOException ;
4
- import java .rmi .RemoteException ;
5
4
import java .util .concurrent .TimeUnit ;
6
- import java .util .concurrent .atomic .AtomicReference ;
5
+ import java .util .concurrent .locks .Condition ;
6
+ import java .util .concurrent .locks .Lock ;
7
+ import java .util .concurrent .locks .ReentrantLock ;
7
8
8
9
import static com .github .akurilov .concurrent .AsyncRunnable .State .FINISHED ;
9
10
import static com .github .akurilov .concurrent .AsyncRunnable .State .INITIAL ;
14
15
public abstract class AsyncRunnableBase
15
16
implements AsyncRunnable {
16
17
17
- private final AtomicReference <State > stateRef = new AtomicReference <>(INITIAL );
18
- protected final Object state = new Object ();
18
+ private volatile State state = State .INITIAL ;
19
+ private final Lock stateLock = new ReentrantLock ();
20
+ protected final Condition stateChanged = stateLock .newCondition ();
19
21
20
22
@ Override
21
23
public final State state () {
22
- return stateRef . get () ;
24
+ return state ;
23
25
}
24
26
25
27
@ Override
26
28
public boolean isInitial () {
27
- return INITIAL . equals ( stateRef . get ()) ;
29
+ return INITIAL == state ;
28
30
}
29
31
30
32
@ Override
31
33
public boolean isStarted () {
32
- return STARTED . equals ( stateRef . get ()) ;
34
+ return STARTED == state ;
33
35
}
34
36
35
37
@ Override
36
38
public boolean isShutdown () {
37
- return SHUTDOWN . equals ( stateRef . get ()) ;
39
+ return SHUTDOWN == state ;
38
40
}
39
41
40
42
@ Override
41
43
public boolean isStopped () {
42
- return STOPPED . equals ( stateRef . get ()) ;
44
+ return STOPPED == state ;
43
45
}
44
46
45
47
@ Override
46
48
public boolean isFinished () {
47
- return FINISHED . equals ( stateRef . get ()) ;
49
+ return FINISHED == state ;
48
50
}
49
51
50
52
@ Override
51
53
public boolean isClosed () {
52
- return null == stateRef . get () ;
54
+ return null == state ;
53
55
}
54
56
55
57
@ Override
56
58
public final AsyncRunnableBase start ()
57
59
throws IllegalStateException {
58
- if (stateRef .compareAndSet (INITIAL , STARTED ) || stateRef .compareAndSet (STOPPED , STARTED )) {
59
- synchronized (state ) {
60
- doStart ();
61
- state .notifyAll ();
60
+ if (stateLock .tryLock ()) {
61
+ try {
62
+ if (state == INITIAL || state == STOPPED ) {
63
+ doStart ();
64
+ state = STARTED ;
65
+ stateChanged .signalAll ();
66
+ } else {
67
+ throw new IllegalStateException (
68
+ "Not allowed to start while state is \" " + state + "\" "
69
+ );
70
+ }
71
+ } finally {
72
+ stateLock .unlock ();
62
73
}
63
74
} else {
64
- throw new IllegalStateException (
65
- "Not allowed to start while state is \" " + stateRef .get () + "\" "
66
- );
75
+ throw new IllegalStateException ("Start: failed to acquire the state lock" );
67
76
}
68
77
return this ;
69
78
}
70
79
71
80
@ Override
72
81
public final AsyncRunnableBase shutdown ()
73
82
throws IllegalStateException {
74
- if (stateRef .compareAndSet (STARTED , SHUTDOWN )) {
75
- synchronized (state ) {
76
- doShutdown ();
77
- state .notifyAll ();
83
+ if (stateLock .tryLock ()) {
84
+ try {
85
+ if (state == STARTED ) {
86
+ doShutdown ();
87
+ state = SHUTDOWN ;
88
+ stateChanged .signalAll ();
89
+ } else {
90
+ throw new IllegalStateException (
91
+ "Not allowed to shutdown while state is \" " + state + "\" "
92
+ );
93
+ }
94
+ } finally {
95
+ stateLock .unlock ();
78
96
}
79
97
} else {
80
- throw new IllegalStateException (
81
- "Not allowed to shutdown while state is \" " + stateRef .get () + "\" "
82
- );
98
+ throw new IllegalStateException ("Shutdown: failed to acquire the state lock" );
83
99
}
84
100
return this ;
85
101
}
86
102
87
103
@ Override
88
104
public final AsyncRunnableBase stop ()
89
- throws IllegalStateException , RemoteException {
90
- try {
91
- shutdown ();
92
- } catch (final IllegalStateException ignored ) {
93
- }
94
- if (stateRef .compareAndSet (STARTED , STOPPED ) || stateRef .compareAndSet (SHUTDOWN , STOPPED )) {
95
- synchronized (state ) {
96
- doStop ();
97
- state .notifyAll ();
105
+ throws IllegalStateException {
106
+ if (stateLock .tryLock ()) {
107
+ try {
108
+ if (state == STARTED || state == SHUTDOWN ) {
109
+ doStop ();
110
+ state = STOPPED ;
111
+ stateChanged .signalAll ();
112
+ } else {
113
+ throw new IllegalStateException (
114
+ "Not allowed to stop while state is \" " + state + "\" "
115
+ );
116
+ }
117
+ } finally {
118
+ stateLock .unlock ();
98
119
}
99
120
} else {
100
- throw new IllegalStateException (
101
- "Not allowed to stop while state is \" " + stateRef .get () + "\" "
102
- );
121
+ throw new IllegalStateException ("Stop: failed to acquire the state lock" );
103
122
}
104
123
return this ;
105
124
}
106
125
107
126
@ Override
108
127
public final AsyncRunnableBase await ()
109
128
throws IllegalStateException , InterruptedException {
110
- await (Long .MAX_VALUE , TimeUnit .DAYS );
129
+ await (Long .MAX_VALUE , TimeUnit .MILLISECONDS );
111
130
return this ;
112
131
}
113
132
114
133
@ Override
115
134
public boolean await (final long timeout , final TimeUnit timeUnit )
116
135
throws IllegalStateException , InterruptedException {
117
- final long timeOutMilliSec = timeUnit .toMillis (timeout );
118
- final long t = System .currentTimeMillis ();
119
- while (isStarted () || isShutdown ()) {
120
- if (System .currentTimeMillis () - t >= timeOutMilliSec ) {
121
- return false ;
122
- }
123
- synchronized (state ) {
124
- state .wait (100 );
136
+ final long invokeTimeMillis = System .currentTimeMillis ();
137
+ final long timeOutMillis = timeUnit .toMillis (timeout );
138
+ long elapsedTimeMillis ;
139
+ while (timeOutMillis > (elapsedTimeMillis = System .currentTimeMillis () - invokeTimeMillis )) {
140
+ if (state != STARTED && state != SHUTDOWN ) {
141
+ return true ; // condition is reached
142
+ } else {
143
+ if (stateLock .tryLock (timeOutMillis - elapsedTimeMillis , TimeUnit .MILLISECONDS )) {
144
+ try {
145
+ // spent a time to wait for the state lock, need to update the elapsed time
146
+ elapsedTimeMillis = System .currentTimeMillis () - invokeTimeMillis ;
147
+ // recheck for the timeout condition
148
+ if (timeOutMillis > elapsedTimeMillis ) {
149
+ if (
150
+ stateChanged .await (
151
+ timeOutMillis - elapsedTimeMillis , TimeUnit .MILLISECONDS
152
+ )
153
+ ) { // the state is changed, recheck the condition
154
+ if (state != STARTED && state != SHUTDOWN ) {
155
+ return true ;
156
+ } // continue otherwise (no timeout yet, condition is not reached)
157
+ }
158
+ } else { // timeout, exit the loop
159
+ break ;
160
+ }
161
+ } finally {
162
+ stateLock .unlock ();
163
+ }
164
+ }
125
165
}
126
166
}
127
- return true ;
167
+ return state != STARTED && state != SHUTDOWN ;
128
168
}
129
169
130
170
@ Override
@@ -136,12 +176,18 @@ public void close()
136
176
} catch (final IllegalStateException ignored ) {
137
177
}
138
178
// then close actually
139
- synchronized (state ) {
140
- if (null != stateRef .get ()) {
141
- doClose ();
142
- stateRef .set (null );
143
- state .notifyAll ();
179
+ if (stateLock .tryLock ()) {
180
+ try {
181
+ if (null != state ) {
182
+ doClose ();
183
+ state = null ;
184
+ stateChanged .signalAll ();
185
+ }
186
+ } finally {
187
+ stateLock .unlock ();
144
188
}
189
+ } else {
190
+ throw new IllegalStateException ("Close: failed to acquire the state lock" );
145
191
}
146
192
}
147
193
0 commit comments