It has been 691 days since the last update, the content of the article may be outdated.
并发问题
- 多个线程同时读写同一个共享变量会存在并发问题
- Immutability模式和Copy-on-Write模式,突破的是写
- ThreadLocal模式,突破的是共享变量
ThreadLocal的使用
线程ID
1 2 3 4 5 6 7 8 9 10
| public class ThreadLocalId { private static final AtomicLong nextId = new AtomicLong(0); private static final ThreadLocal<Long> TL = ThreadLocal.withInitial( () -> nextId.getAndIncrement());
private static long get() { return TL.get(); } }
|
1 2 3 4 5 6 7 8
| public class SafeDateFormat { private static final ThreadLocal<DateFormat> TL = ThreadLocal.withInitial( () -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
private static DateFormat get() { return TL.get(); } }
|
ThreadLocal的工作原理
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
| public class Thread implements Runnable { ThreadLocal.ThreadLocalMap threadLocals = null; }
public class ThreadLocal<T> { public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
static class ThreadLocalMap { private Entry[] table; private Entry getEntry(ThreadLocal<?> key) { }
static class Entry extends WeakReference<ThreadLocal<?>> { Object value; } } }
|
- ThreadLocal仅仅是一个代理工具类,内部并不持有任何与线程相关的数据(存储在Thread里面)
- 不容易产生内存泄露,Thread持有ThreadLocalMap,而ThreadLocalMap对ThreadLocal的引用是弱引用
- 只要Thread被回收,那么ThreadLocalMap就能被回收
- 在线程池中使用ThreadLocal也有可能会导致内存泄露
- 因为线程池中线程的存活时间太长了,往往与程序同生共死
- 意味着Thread持有的ThreadLocalMap一直都不会被回收
- 可以通过try/finally手动释放
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private static final ExecutorService pool = Executors.newFixedThreadPool(1); private static final ThreadLocal<Object> TL = ThreadLocal.withInitial(() -> new Object());
public static void main(String[] args) {
pool.execute(() -> { try { TL.set(new Object()); } finally { TL.remove(); } }); }
|
InheritableThreadLocal
- 通过ThreadLocal创建的线程变量,其子线程是无法继承的,如果需要继承,则采用InheritableThreadLocal
- 不建议在线程池中使用InheritableThreadLocal
- 一方面,InheritableThreadLocal具有和ThreadLocal相同的缺点,可能会导致内存泄露
- 另一方面,线程池中的线程创建是动态的,容易导致继承关系错乱
- 如果业务逻辑依赖InheritableThreadLocal,有可能导致业务逻辑计算错误,比内存泄露更致命
参考资料
Java并发编程实战