1. ThreadLocal 的底层实现
原理
ThreadLocal 变量是线程独享的,每个线程都有一个 ThreadLocalMap 变量,该变量存储了 ThreadLocal 实例作为 key,对应的值作为 value。
每个线程(Thread)内部有一个 ThreadLocalMap,而 ThreadLocal 只是一个 key,本身不存储数据。
源码解析 ThreadLocal 通过 ThreadLocalMap 存储数据,以下是 set() 方法的实现:
1 2 3 4 5 6 7 8 9 public void set (T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null ) { map.set(this , value); } else { createMap(t, value); } }
get() 方法
1 2 3 4 5 6 7 8 9 10 11 public T get () { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null ) { ThreadLocalMap.Entry e = map.getEntry(this ); if (e != null ) { return (T) e.value; } } return null ; }
示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class ThreadLocalDemo { private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0 ); public static void main (String[] args) { new Thread (() -> { threadLocal.set(10 ); System.out.println(Thread.currentThread().getName() + " : " + threadLocal.get()); }).start(); new Thread (() -> { threadLocal.set(20 ); System.out.println(Thread.currentThread().getName() + " : " + threadLocal.get()); }).start(); } }
输出
1 2 Thread-0 : 10 Thread-1 : 20
不同线程存储的 threadLocal
值互不影响。
2. ThreadLocal 为什么会导致内存泄漏 原因 ThreadLocalMap 的 key 是弱引用,但 value 是强引用,如果没有手动清除,可能导致 value 无法被回收,从而引发内存泄漏。
如何避免
使用完手动 remove()
尽量使用 static 修饰的 ThreadLocal
减少使用 ThreadLocal 保存大对象
3. sleep() 与 wait() 区别
区别点
sleep()
wait()
所属类
Thread
Object
是否释放锁
不释放锁
释放锁
使用位置
可在任何地方使用
必须在 synchronized
代码块中
如何唤醒
时间到自动唤醒
notify()
或 notifyAll()
唤醒
线程状态
TIMED_WAITING
WAITING
示例 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 public class SleepWaitDemo { public static void main (String[] args) { Object lock = new Object (); Thread t1 = new Thread (() -> { synchronized (lock) { try { System.out.println("Thread1: waiting..." ); lock.wait(); System.out.println("Thread1: resumed" ); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2 = new Thread (() -> { synchronized (lock) { try { System.out.println("Thread2: sleeping..." ); Thread.sleep(2000 ); System.out.println("Thread2: notifying..." ); lock.notify(); } catch (InterruptedException e) { e.printStackTrace(); } } }); t1.start(); t2.start(); } }
输出
1 2 3 4 Thread1: waiting... Thread2: sleeping... Thread2: notifying... Thread1: resumed
t1
释放锁等待,t2
2秒后唤醒 t1
。
4. 如何保证多个线程顺序执行 方法
使用 join()
1 2 3 4 5 Thread t1 = new Thread (() -> System.out.println("任务 A" ));Thread t2 = new Thread (() -> System.out.println("任务 B" ));t1.start(); t1.join(); t2.start();
单线程池
1 2 3 4 ExecutorService executor = Executors.newSingleThreadExecutor();executor.submit(() -> System.out.println("任务 A" )); executor.submit(() -> System.out.println("任务 B" )); executor.shutdown();
CountDownLatch
1 2 3 4 5 6 7 8 9 10 11 12 13 CountDownLatch latch = new CountDownLatch (1 );new Thread (() -> { System.out.println("任务 A" ); latch.countDown(); }).start(); new Thread (() -> { try { latch.await(); System.out.println("任务 B" ); } catch (InterruptedException e) { e.printStackTrace(); } }).start();
5. submit() 和 execute() 区别
区别
区别点
execute()
submit()
提交方式
只能提交 Runnable
可提交 Runnable
或 Callable
是否有返回值
无
有返回值 Future
异常处理
直接抛出异常
需要调用 future.get()
才能获取异常
示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ExecutorService executor = Executors.newFixedThreadPool(2 );executor.execute(() -> { System.out.println("执行任务 A" ); }); Future<Integer> future = executor.submit(() -> { System.out.println("执行任务 B" ); return 42 ; }); try { System.out.println("任务 B 结果: " + future.get()); } catch (Exception e) { e.printStackTrace(); } executor.shutdown();
输出
1 2 3 执行任务 A 执行任务 B 任务 B 结果: 42
总结 ➢ (1) 两个方法都可以向线程池提交任务; ➢ (2) execute只能提交Runnable,无返回值; ➢ (3) submit既可以提交Runnable,返回值为null,也可以提交Callable,返回值Future; ➢ (4) execute()方法定义在Executor接口中; ➢ (5) submit()方法定义在ExecutorService接口中; ➢ (6) execute执行任务时遇到异常会直接抛出; ➢ (7) submit执行任务时遇到异常不会直接抛出,只有在调用Future的get()方法获取返回值时,才会抛出异常;
execute()
适用于 不关心返回值 的任务。
submit()
适用于 需要获取结果 或 异常处理 的任务。
总结
ThreadLocal 通过 ThreadLocalMap
维护线程私有数据,防止多线程共享变量干扰。
ThreadLocal 可能导致内存泄漏,要手动 remove()
释放资源。
sleep() 不释放锁,wait() 释放锁,wait() 需要 notify()
唤醒。
保证线程顺序执行的方法有 join()、单线程池、CountDownLatch 等。
submit() 可返回 Future,execute() 不能返回结果。