A thread-safe mutable-value container inspired by Clojure's agents and GPars' Agent. An Agent wraps a value that can be read by any thread but modified only through serialized update functions. Updates are queued and applied one at a time on a dedicated executor, guaranteeing tha
| 75 | * @since 6.0.0 |
| 76 | */ |
| 77 | public final class Agent<T> { |
| 78 | |
| 79 | /** Default per-subscriber buffer size for {@link #changes()}. */ |
| 80 | private static final int DEFAULT_CHANGES_BUFFER = 256; |
| 81 | |
| 82 | private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); |
| 83 | private final ExecutorService updateExecutor; |
| 84 | private final Object lifecycleLock = new Object(); |
| 85 | private volatile T value; |
| 86 | private volatile SubmissionPublisher<T> changesPublisher; |
| 87 | private volatile boolean shutdownInvoked; |
| 88 | |
| 89 | private Agent(T initialValue, ExecutorService executor) { |
| 90 | this.value = initialValue; |
| 91 | this.updateExecutor = executor; |
| 92 | } |
| 93 | |
| 94 | /** |
| 95 | * Creates an agent with the given initial value, using a |
| 96 | * single-thread executor for serialized updates. |
| 97 | * |
| 98 | * @param initialValue the starting value |
| 99 | * @param <T> the value type |
| 100 | * @return a new agent |
| 101 | */ |
| 102 | public static <T> Agent<T> create(T initialValue) { |
| 103 | return new Agent<>(initialValue, |
| 104 | Executors.newSingleThreadExecutor(r -> { |
| 105 | Thread t = new Thread(r, "groovy-agent"); |
| 106 | t.setDaemon(true); |
| 107 | return t; |
| 108 | })); |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * Creates an agent backed by the given pool for update execution. |
| 113 | * Updates are still serialized (only one at a time), but they run |
| 114 | * on the pool's threads. |
| 115 | * |
| 116 | * @param initialValue the starting value |
| 117 | * @param pool the pool to use for updates |
| 118 | * @param <T> the value type |
| 119 | * @return a new agent |
| 120 | */ |
| 121 | public static <T> Agent<T> create(T initialValue, Pool pool) { |
| 122 | Objects.requireNonNull(pool, "pool must not be null"); |
| 123 | // Use a SerialExecutor to serialize updates on the pool's threads. |
| 124 | // We cannot use newSingleThreadExecutor with a delegating ThreadFactory |
| 125 | // because that breaks the executor's internal task loop. |
| 126 | return new Agent<>(initialValue, new SerialExecutor(pool)); |
| 127 | } |
| 128 | |
| 129 | /** |
| 130 | * Returns the current value. This is a non-blocking snapshot read. |
| 131 | * |
| 132 | * @return the current value |
| 133 | */ |
| 134 | public T get() { |
nothing calls this directly
no outgoing calls
no test coverage detected