ThreadLocal

概述

ThreadLocal提供线程局部变量。
这些变量与正常的变量不同,因为每一个线程在访问ThreadLocal实例的时候(通过其get或set方法)都有自己的、独立初始化的变量副本。
ThreadLocal实例通常是类中的私有静态字段,使用它的目的是希望将状态(例如,用户ID或事务ID)与线程关联起来。
实现每一个线程都有自己专属的本地变量副本(自己用自己的变量不麻烦别人,不和其他人共享,人人有份,人各一份),主要解决了让每个线程绑定自己的值,通过使用get()和set()方法,获取默认值或将其值更改为当前线程所存的副本的值从而避免了线程安全问题。
notion image

API

notion image

案例

三个售票员卖完50张票务,总量完成即可,吃大锅饭,售票员每个月固定月薪
class MovieTicket { int number = 50; public synchronized void saleTicket() { if(number > 0) { System.out.println(Thread.currentThread().getName()+"\t"+"号售票员卖出第: "+(number--)); }else{ System.out.println("--------卖完了"); } } } /** * @create 2021-03-23 15:03 * */ public class ThreadLocalDemo { public static void main(String[] args) { MovieTicket movieTicket = new MovieTicket(); for (int i = 1; i <=3; i++) { new Thread(() -> { for (int j = 0; j <20; j++) { movieTicket.saleTicket(); try { TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } },String.valueOf(i)).start(); } } }
不参加总和计算,希望各自分灶吃饭,各凭销售本事提成,按照出单数各自统计
class House { ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0); public void saleHouse() { Integer value = threadLocal.get(); value++; threadLocal.set(value); } } /** * * 2 分灶吃饭,各个销售自己动手,丰衣足食 */ public class ThreadLocalDemo { public static void main(String[] args) { House house = new House(); new Thread(() -> { try { for (int i = 1; i <=3; i++) { house.saleHouse(); } System.out.println(Thread.currentThread().getName()+"\t"+"---"+house.threadLocal.get()); }finally { house.threadLocal.remove();//如果不清理自定义的 ThreadLocal 变量,可能会影响后续业务逻辑和造成内存泄露等问题 } },"t1").start(); new Thread(() -> { try { for (int i = 1; i <=2; i++) { house.saleHouse(); } System.out.println(Thread.currentThread().getName()+"\t"+"---"+house.threadLocal.get()); }finally { house.threadLocal.remove(); } },"t2").start(); new Thread(() -> { try { for (int i = 1; i <=5; i++) { house.saleHouse(); } System.out.println(Thread.currentThread().getName()+"\t"+"---"+house.threadLocal.get()); }finally { house.threadLocal.remove(); } },"t3").start(); System.out.println(Thread.currentThread().getName()+"\t"+"---"+house.threadLocal.get()); } }
notion image
因为每个 Thread 内有自己的实例副本且该副本只由当前线程自己使用 既然其它 Thread 不可访问,那就不存在多线程间共享的问题。统一设置初始值,但是每个线程对这个值的修改都是各自线程互相独立的