Class ThreadLocal<T>

本文深入探讨了ThreadLocal的工作原理及其实现细节,并通过具体示例展示了如何利用ThreadLocal为每个线程提供独立的变量副本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
这个类提供了线程局部变量。这些变量不同于他们一般的对应物,因为访问ThreadLocal变量(通过变量的get或set方法)的每个线程都有自己的,独立于ThreadLocal变量初始化的拷备。ThreadLocal实例在类中一般为私有的静态变量,为的是将状态与线程关联。(这段是我自己翻译的,仅做参考)

For example, in the class below, the private static ThreadLocal instance (serialNum) maintains a "serial number" for each thread that invokes the class's static SerialNum.get() method, which returns the current thread's serial number. (A thread's serial number is assigned the first time it invokes SerialNum.get(), and remains unchanged on subsequent calls.)
例如,在下面的类中,这个私有的静态ThreadLocal实例(serialNum)向每个调用这个类的静态SerialNum.get()方法的线程提供一个“序列号”,这个方法返回当前线程的序列号。(一个线程的序列号在第一次调用SerialNum.get()时指定,而且在后续调用中保持不变)

public class SerialNum {
     // The next serial number to be assigned
     private static int nextSerialNum = 0;

     private static ThreadLocal serialNum = new ThreadLocal() {
         protected synchronized Object initialValue() {
             return new Integer(nextSerialNum++);
         }
     };

     public static int get() {
         return ((Integer) (serialNum.get())).intValue();
     }
    
     public static void printNextSerialNum(){
                System.out.println("nextSerialNum :" + nextSerialNum);
        }
}


Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).
只要线程活动着并且ThreadLocal实例是可访问的,线程都会持有一个ThreadLocal变量的隐式引用。当线程结束时,所有的ThreadLocal实例副本都会被垃圾回收(除非存在对这些副本的其它引用)

ThreadLocal的核心思想很简单:为每个独立的线程提供一个变量的副本。
ThreadLocal使用了“拷贝副本”的方式,人人有份,你用你的,我用我的,大家互不影响,是“以空间换时间”。每个线程修改变量时,实际上修改的是变量的副本,不怕影响到其它线程。

为了加深对ThreadLocal的理解,我修改了上面的例子:

1】SerialNum类
public class SerialNum {
   
    private static ThreadLocal<Integer> serialNum = new ThreadLocal<Integer>() {
        protected synchronized Integer initialValue() {
            return new Integer(1);
        }
       
    };
   
    private static Integer num ;

    public static int get() {
        return ((Integer) (serialNum.get())).intValue();
    }
   
    public static void set(int i){
        serialNum.set(new Integer(i));
    }

    public static Integer getNum() {
        return num;
    }

    public static void setNum(Integer num) {
        SerialNum.num = num;
    }
   
    public static void printInfo(){
        System.out.println("SerialNum 序列号 :"+SerialNum.get());
        System.out.println("SerialNum num = :"+num);
    }
}

2】GetSerialNumThread
public class SerialNumMain {
    public static void main(String[] args){
        SerialNumMain sn = new SerialNumMain();
        SerialNumThread st1 = sn.new SerialNumThread("st1",11,111);
        SerialNumThread st2 = sn.new SerialNumThread("st2",22,222);
       
        st1.start();
        try {
            st1.join();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
       
        System.out.println("=============================");
       
        st2.start();
        try {
            st2.join();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
       
        System.out.println("=============================");
        System.out.println("当前线程查看SerialNum中属性的变化:");
        SerialNum.printInfo();
    }
   
    class SerialNumThread extends Thread{
        private String threadName;
        private int newSerialNum;
        private int num;
        public SerialNumThread(String threadName, int newSerialNum, int num){
            super();
            this.threadName = threadName;
            this.newSerialNum = newSerialNum;
            this.num = num;
        }
        @Override
        public void run(){
            System.out.println("线程"+threadName+" 序列号 :"+SerialNum.get());
            System.out.println("线程"+threadName+" num = :"+SerialNum.getNum());
           
            System.out.println("修改序列号......");
            changeSerialNum();
            System.out.println("修改num......");
            changeNum();
           
            System.out.println("线程"+threadName+" 修改后的 序列号 :"+SerialNum.get());
            System.out.println("线程"+threadName+" 修改后的 num = :"+SerialNum.getNum());
        }
        public void changeSerialNum(){
            SerialNum.set(newSerialNum);
        }
        public void changeNum(){
            SerialNum.setNum(num);
        }
    }
}

运行的结果如下:

线程st1 序列号 :1
线程st1 num = :null
修改序列号......
修改num......
线程st1 修改后的 序列号 :11
线程st1 修改后的 num = :111
=============================
线程st2 序列号 :1
线程st2 num = :111
修改序列号......
修改num......
线程st2 修改后的 序列号 :22
线程st2 修改后的 num = :222
=============================
当前线程查看SerialNum中属性的变化:
SerialNum 序列号 :1
SerialNum num = :222

注意比较变量serialNum和num。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值