TheadLocal类学习

ThreadLocal 是 Java 中一个非常实用的线程相关的类,它提供线程本地变量,即每个线程都有自己独立的变量副本,从而避免了线程安全问题。下面我将通过几个方面来帮助你理解并学习如何使用 ThreadLocal

基本概念

  • 线程局部变量:每个线程都拥有一份 ThreadLocal 变量的副本,彼此之间互不影响,这使得在多线程环境下可以做到数据隔离,提高程序的并发能力
  • 应用场景:适用于每个线程需要独立的资源或状态的情况,比如数据库连接、事务管理、线程专属的缓存、记录日志时的线程ID等。

使用步骤

  1. 创建 ThreadLocal 实例:指定泛型类型,表示存储在其中的变量类型。
  2. 设置值:通过 set 方法为当前线程设置值。
  3. 获取值:通过 get 方法获取当前线程对应的值。
  4. 清理:使用完毕后,可以通过 remove 方法清理当前线程的值,避免内存泄漏。

例:应用:如何使用ThreadLocal为每个线程创建独立的计数器

 1 import java.util.concurrent.ExecutorService;
 2 import java.util.concurrent.Executors;
 3 
 4 
 5 /**
 6  * 在这个示例中,尽管使用了ThreadLocal来存储每个线程的计数,但由于线程池中的线程会被复用,
 7  * 当一个线程完成一个任务并开始处理下一个任务时,之前设置在ThreadLocal中的计数值没有被清除。
 8  *
 9  * 因此,第二个任务开始时,它可能会继承上一个任务在该线程中设置的计数,导致数据看起来像是“交叉污染”。
10  */
11 public class ThreadLocalMisuseExample {
12 
13     // 静态ThreadLocal变量,用于存储当前线程的计数
14     private static final ThreadLocal<Integer> threadCounter = ThreadLocal.withInitial(() -> 0);  // 或  new ThreadLocal<>();但是为初始化为0
15 
16     public static void main(String[] args) {
17         ExecutorService executor = Executors.newFixedThreadPool(2); // 创建固定大小的线程池
18 
19         // 提交任务到线程池
20         for (int i = 0; i < 4; i++) {
21             int taskId = i;
22             executor.submit(() -> {
23                 Integer count = threadCounter.get();
24                 System.out.println("Task ID " + taskId + " on thread " + Thread.currentThread().getName()
25                         + ", initial count: " + count);
26                 threadCounter.set(count + 1); // 增加计数
27                 System.out.println("Task ID " + taskId + " on thread " + Thread.currentThread().getName()
28                         + ", updated count: " + threadCounter.get());
29 
30                 // 修正交叉污染问题:清除ThreadLocal变量,防止数据污染
31                // threadCounter.remove();
32             });
33         }
34 
35         executor.shutdown();
36     }
37 }

 

结果1:

Task ID 1 on thread pool-1-thread-2, initial count: 0
Task ID 0 on thread pool-1-thread-1, initial count: 0
Task ID 1 on thread pool-1-thread-2, updated count: 1
Task ID 0 on thread pool-1-thread-1, updated count: 1
Task ID 2 on thread pool-1-thread-1, initial count: 1
Task ID 2 on thread pool-1-thread-1, updated count: 2
Task ID 3 on thread pool-1-thread-2, initial count: 1
Task ID 3 on thread pool-1-thread-2, updated count: 2

 

结果2: 即把31行 注释放开 // threadCounter.remove();

--修正后,每个Task的两个thread计数:initial为0、updated为1

Task ID 0 on thread pool-1-thread-1, initial count: 0
Task ID 1 on thread pool-1-thread-2, initial count: 0
Task ID 1 on thread pool-1-thread-2, updated count: 1
Task ID 0 on thread pool-1-thread-1, updated count: 1
Task ID 2 on thread pool-1-thread-2, initial count: 0
Task ID 3 on thread pool-1-thread-1, initial count: 0
Task ID 3 on thread pool-1-thread-1, updated count: 1
Task ID 2 on thread pool-1-thread-2, updated count: 1

 

例2:其它使用ThreadLocal导致的串号问题:如  Server通常使用ThreadPool来接受处理请求,而ThreadPool中的Thread是复用的。

 

posted on 2024-05-07 15:47  gogoy  阅读(10)  评论(0编辑  收藏  举报

导航