-
Notifications
You must be signed in to change notification settings - Fork 26
Expand file tree
/
Copy pathAOThread.java
More file actions
132 lines (115 loc) · 3.62 KB
/
AOThread.java
File metadata and controls
132 lines (115 loc) · 3.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/**
* @author: Damir Ljubic
* @email: damirlj@yahoo.com
* <p>All rights reserved!
*/
package <your own package>;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public final class AOThread {
// Single-thread execution context
private final ExecutorService executionContext = Executors.newSingleThreadExecutor();
private final Looper looper = new Looper();
/** Start the AOT: Attach the Looper with the execution context */
public void start() {
executionContext.submit(looper.getLooper());
}
@FunctionalInterface
private interface IStopThread {
void stop(@NotNull ExecutorService executor);
}
/** Stop thread which drains the message queue */
private void stop(@NotNull IStopThread callback) throws InterruptedException {
looper.stop(); // set the exit flag
callback.stop(executionContext);
}
/**
* Try to stop the background AOT gracefully, by waiting on the last task completion
*
* @param timeout The timeout to wait for
* @param unit The time unit for the timeout to be expressed with
*/
public void stop(long timeout, TimeUnit unit) throws InterruptedException {
stop(
executor -> {
executor.shutdown(); // initiate shutdown
try {
if (!executor.awaitTermination(timeout, unit)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
});
}
/**
* Stop the AOT background thread immediately, without waiting on the task completion
*
* @note There is no guarantees that the last running task will be successfully interrupted
*/
public void stopNow() throws InterruptedException {
stop(ExecutorService::shutdownNow);
}
private void submit(@NotNull Runnable task) throws InterruptedException {
looper.submit(task);
}
/**
* Submit the task to the Looper (job-queue). <br>
* The client will be synchronized on the result of the execution.
*
* @param job The task to be enqueued
* @return The future object - for synchronizing on the execution outcome
* @param <R> The result type
*/
@NotNull
public <R> Optional<CompletableFuture<R>> enqueueWithResult(@NotNull Callable<R> job) {
CompletableFuture<R> f = new CompletableFuture<>();
// Wrap the callable into parameterless runnable task
Runnable task =
() -> {
try {
R result = job.call();
f.complete(result); // signal the result to client
} catch (Exception e) {
f.completeExceptionally(e); // or signal the exception to client
}
};
try {
submit(task);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return Optional.empty();
}
return Optional.of(f);
}
@FunctionalInterface
public interface IJob {
void execute() throws Exception;
}
/**
* Submit the job to the Looper. <br>
* Fire-and-forget: client will not wait on the outcome of execution, if any.
*
* @param job The task to be executed
*/
public void enqueue(@NotNull IJob job) {
Runnable task = ()->{
try {
job.execute();
} catch(Exception e) {
e.printStackTrace(); // your own logging
}
};
try {
submit(task);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}