forked from alibaba/transmittable-thread-local
-
Notifications
You must be signed in to change notification settings - Fork 1
/
TtlRunnable.java
158 lines (145 loc) · 6.78 KB
/
TtlRunnable.java
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package com.alibaba.ttl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.Collections;
/**
* {@link TtlRunnable} decorate {@link Runnable}, so as to get {@link TransmittableThreadLocal}
* and transmit it to the time of {@link Runnable} execution, needed when use {@link Runnable} to thread pool.
* <p>
* Use factory method {@link #get(Runnable)} to create instance.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @see java.util.concurrent.Executor
* @see java.util.concurrent.ExecutorService
* @see java.util.concurrent.ThreadPoolExecutor
* @see java.util.concurrent.ScheduledThreadPoolExecutor
* @see java.util.concurrent.Executors
* @since 0.9.0
*/
public final class TtlRunnable implements Runnable {
private final AtomicReference<Map<TransmittableThreadLocal<?>, Object>> copiedRef;
private final Runnable runnable;
private final boolean releaseTtlValueReferenceAfterRun;
private TtlRunnable(Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
this.copiedRef = new AtomicReference<Map<TransmittableThreadLocal<?>, Object>>(TransmittableThreadLocal.copy());
this.runnable = runnable;
this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
}
/**
* wrap method {@link Runnable#run()}.
*/
@Override
public void run() {
Map<TransmittableThreadLocal<?>, Object> copied = copiedRef.get();
if (copied == null || releaseTtlValueReferenceAfterRun && !copiedRef.compareAndSet(copied, null)) {
throw new IllegalStateException("TTL value reference is released after run!");
}
Map<TransmittableThreadLocal<?>, Object> backup = TransmittableThreadLocal.backupAndSet(copied);
try {
runnable.run();
} finally {
TransmittableThreadLocal.restore(backup);
}
}
/**
* return original/unwrapped {@link Runnable}.
*/
public Runnable getRunnable() {
return runnable;
}
/**
* Factory method, wrapper input {@link Runnable} to {@link TtlRunnable}.
* <p>
* This method is idempotent.
*
* @param runnable input {@link Runnable}. if input is {@code null}, return {@code null}.
* @return Wrapped {@link Runnable}
* @throws IllegalStateException when input is {@link TtlRunnable} already.
*/
public static TtlRunnable get(Runnable runnable) {
return get(runnable, false, false);
}
/**
* Factory method, wrapper input {@link Runnable} to {@link TtlRunnable}.
* <p>
* This method is idempotent.
*
* @param runnable input {@link Runnable}. if input is {@code null}, return {@code null}.
* @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
* @return Wrapped {@link Runnable}
* @throws IllegalStateException when input is {@link TtlRunnable} already.
*/
public static TtlRunnable get(Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
return get(runnable, releaseTtlValueReferenceAfterRun, false);
}
/**
* Factory method, wrapper input {@link Runnable} to {@link TtlRunnable}.
* <p>
* This method is idempotent.
*
* @param runnable input {@link Runnable}. if input is {@code null}, return {@code null}.
* @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
* @param idempotent is idempotent or not. if {@code }, return input object when it's {@link TtlRunnable}, or throw {@link IllegalStateException}.
* {@code true} will cover up bugs! <b>DO NOT</b> set, only when you know why.
* @return Wrapped {@link Runnable}
* @throws IllegalStateException
*/
public static TtlRunnable get(Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
if (null == runnable) {
return null;
}
if (runnable instanceof TtlRunnable) {
if (idempotent) {
// avoid redundant decoration, and ensure idempotency
return (TtlRunnable) runnable;
} else {
throw new IllegalStateException("Already TtlRunnable!");
}
}
return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun);
}
/**
* wrapper input {@link Runnable} Collection to {@link TtlRunnable} Collection.
*
* @param tasks task to be wrapped. if input is {@code null}, return {@code null}.
* @return wrapped tasks
* @throws IllegalStateException when input is {@link TtlRunnable} already.
*/
public static List<TtlRunnable> gets(Collection<? extends Runnable> tasks) {
return gets(tasks, false, false);
}
/**
* wrapper input {@link Runnable} Collection to {@link TtlRunnable} Collection.
*
* @param tasks task to be wrapped. if input is {@code null}, return {@code null}.
* @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
* @return wrapped tasks
* @throws IllegalStateException when input is {@link TtlRunnable} already.
*/
public static List<TtlRunnable> gets(Collection<? extends Runnable> tasks, boolean releaseTtlValueReferenceAfterRun) {
return gets(tasks, releaseTtlValueReferenceAfterRun, false);
}
/**
* wrapper input {@link Runnable} Collection to {@link TtlRunnable} Collection.
*
* @param tasks task to be wrapped. if input is {@code null}, return {@code null}.
* @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred.
* @param idempotent is idempotent or not. if {@code }, return input object when it's {@link TtlRunnable}, or throw {@link IllegalStateException}.
* {@code true} will cover up bugs! <b>DO NOT</b> set, only when you know why.
* @return wrapped tasks
* @throws IllegalStateException
*/
public static List<TtlRunnable> gets(Collection<? extends Runnable> tasks, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
if (null == tasks) {
return Collections.emptyList();
}
List<TtlRunnable> copy = new ArrayList<TtlRunnable>();
for (Runnable task : tasks) {
copy.add(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent));
}
return copy;
}
}