Skip to content

Commit 13d699e

Browse files
committed
Add Code Smells section, ThreadLocal section
1 parent 9d6cec0 commit 13d699e

File tree

1 file changed

+65
-1
lines changed

1 file changed

+65
-1
lines changed

README.md

+65-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ Design
1010
- Instance confinement
1111
- Thread/Task/Serial thread confinement
1212
- Active object
13+
- Code smells, identifying that a class or a subsystem could potentially be redesigned for better:
14+
- [Usage of `synchronized` with `wait`/`notify` instead of concurrency utilities
15+
](#avoid-wait-notify)
16+
- [Nested critical sections](#avoid-nested-critical-sections)
17+
- [Extension API call within a critical section](#non-open-call)
18+
- [Large critical section](#minimize-critical-sections)
19+
- [Waiting in a loop for some result](#justify-busy-wait)
20+
- [`ThreadLocal`, especially when non-static](#threadlocal-design)
1321

1422
Documentation
1523
- [Thread safety is justified in comments?](#justify-document)
@@ -172,6 +180,15 @@ Time
172180
](#time-going-backward)
173181
- [Units for a time variable are identified in the variable's name or via `TimeUnit`?](#time-units)
174182
- [Negative timeouts and delays are treated as zeros?](#treat-negative-timeout-as-zero)
183+
184+
`ThreadLocal`
185+
- [`ThreadLocal` can be `static final`?](#tl-static-final)
186+
- [Can redesign a subsystem to avoid usage of `ThreadLocal` (esp. non-static one)?
187+
](#threadlocal-design)
188+
- [`ThreadLocal` is *not* used just to avoid moderate amount of allocation?
189+
](#threadlocal-performance)
190+
- [Considered replacing a non-static `ThreadLocal` with an instance-confined `Map<Thread, ...>`?
191+
](#tl-instance-chm)
175192

176193
Thread safety of Cleaners and native code
177194
- [`close()` is concurrently idempotent in a class with a `Cleaner` or `finalize()`?
@@ -216,7 +233,8 @@ https://en.wikipedia.org/wiki/Staged_event-driven_architecture).
216233
**Instance confinement.** Objects of some root type encapsulate some complex hierarchical child
217234
state. Root objects are solitarily responsible for the safety of accesses and modifications to the
218235
child state from multiple threads. In other words, composed objects are synchronized rather than
219-
synchronized objects are composed. See [JCIP 4.2, 10.1.3, 10.1.4].
236+
synchronized objects are composed. See [JCIP 4.2, 10.1.3, 10.1.4]. [TL.4](#tl-instance-chm) touches
237+
on instance confinement of thread-local state.
220238

221239
**Thread/Task/Serial thread confinement.** Some state is made local to a thread using top-down
222240
pass-through parameters or `ThreadLocal`. See [JCIP 3.3]. Task confinement is a variation of the
@@ -1310,6 +1328,52 @@ parameter next to a "timeout" parameter. This is the preferred option for public
13101328
treat negative arguments as zeros?** This is to obey the principle of least astonishment because all
13111329
timed blocking methods in classes from `java.util.concurrent.*` follow this convention.
13121330

1331+
### `ThreadLocal`
1332+
1333+
<a name="tl-static-final"></a>
1334+
[#](#tl-static-final) TL.1. **Can `ThreadLocal` field be `static final`?** There are three cases
1335+
when a `ThreadLocal` cannot be static:
1336+
- It *holds some state specific to the containing instance object*, rather than, for example,
1337+
reusable objects to avoid allocations (which would be the same for all `ThreadLocal`-containing
1338+
instances).
1339+
- A method using a `ThreadLocal` may call another method (or the same method, recursively) that also
1340+
uses this `ThreadLocal`, but on a different containing object.
1341+
- There is a class (or `enum`) modelling a specific type of `ThreadLocal` usage, and there are only
1342+
limited number of instances of this class in the JVM: i. e. all are constants stored in
1343+
`static final` fields, or `enum` constants.
1344+
1345+
If a usage of `ThreadLocal` doesn't fall into either of these categories, it can be `static final`.
1346+
1347+
There is an inspection "ThreadLocal field not declared static final" in IntelliJ IDEA which
1348+
corresponds to this item.
1349+
1350+
<a name="threadlocal-design"></a>
1351+
[#](#threadlocal-design) TL.2. Doesn't a **`ThreadLocal` mask issues with the code, such as poor
1352+
control flow or data flow design?** Is it possible to redesign the system without using
1353+
`ThreadLocal`, would that be simpler? This is especially true for instance-level (non-static)
1354+
`ThreadLocal` fields; see also [TL.1](#tl-static-final) and [TL.4](#tl-instance-chm) about them.
1355+
1356+
See [Dc.2](#threading-flow-model) about the importance of articulating the control flow and the data
1357+
flow of a subsystem which may help to uncover other issues with the design.
1358+
1359+
<a name="threadlocal-performance"></a>
1360+
[#](#threadlocal-performance) TL.3. Isn't a **`ThreadLocal` used only to reuse some small heap
1361+
objects, cheap to allocate and initialize, that would otherwise need to be allocated relatively
1362+
infrequently?** In this case, the cost of accessing a `ThreadLocal` would likely outweigh the
1363+
benefit from reducing allocations. The evidence should be supplied that introducing a `ThreadLocal`
1364+
shortens the GC pauses and/or increases the overall throughput of the system.
1365+
1366+
<a name="tl-instance-chm"></a>
1367+
[#](#tl-instance-chm) TL.4. If the threads which execute code with usage of a non-static
1368+
`ThreadLocal` are long-living and there is a fixed number of them (e. g. workers of a fixed-sized
1369+
`ThreadPoolExecutor`) and there is a greater number of shorter-living `ThreadLocal`-containing
1370+
objects, was it considered to **replace the instance-level `ThreadLocal<Val>` with a
1371+
`ConcurrentHashMap<Thread, Val> threadLocalValues` confined to the objects**, accessed via like
1372+
`threadLocalValues.get(Thread.currentThread())`? This approach requires some confidence and
1373+
knowledge about the threading model of the subsystem (see [Dc.2](threading-flow-model)), though it
1374+
may also be trivial if Active Object pattern is used (see [Dn.2](#use-patterns)), but is much
1375+
friendlier to GC because no short-living weak references are produced.
1376+
13131377
### Thread safety of Cleaners and native code
13141378

13151379
<a name="thread-safe-close-with-cleaner"></a>

0 commit comments

Comments
 (0)