欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java中ThreadLocal对象理解和使用

程序员文章站 2022-07-10 20:22:56
...

对ThreadLocal的理解

**个人理解:**用一个ThreadLocal可以用来为每个线程独立存储一个对象,让每个线程都有唯一的副本。每一个线程读取的变量是对应的互相独立的。通过get和set方法就可以得到当前线程对应的值。

如果单单从使用的角度可以理解为(实际不是这样):
ThreadLocal相当于维护了一个map,key就是当前的线程,value就是需要存储的对象。

实际存储结构是:
线程Thread中维护了一个ThreadLocalMap对象,ThreadLocalMap对象是以ThreadLocal作为key,存储数据。一个Thread中可能会定义多个ThreadLocal对象。



使用ThreadLocal

代码如下:

package com.study.threadLocal使用;

import java.util.Random;

import org.apache.commons.lang3.StringUtils;

public class TestThreadLocal2 implements Runnable
{
	
    private static final ThreadLocal<TestObject> formatter = ThreadLocal.withInitial(() -> new TestObject());
    /*
     *                          ||
     */
    private static final ThreadLocal<TestObject> formatter2 = new ThreadLocal<TestObject>(){
        @Override
        protected TestObject initialValue()
        {
        	TestObject obj = new TestObject();
            return obj;
        }
    };
    
    public static void main(String[] args) throws InterruptedException {
        TestThreadLocal2 obj = new TestThreadLocal2();
        for(int i=0 ; i<3; i++){
            Thread t = new Thread(obj, ""+i);
            Thread.sleep(new Random().nextInt(1000));
            t.start();
        }
    }

    @Override
    public void run() {
    	String name = Thread.currentThread().getName();
    	if(StringUtils.equals(name, "0")){
    		TestObject obj = formatter.get();
    		obj.setName("小白");
            formatter.set(obj);
    	}
    	if(StringUtils.equals(name, "1")){
            TestObject obj = formatter.get();
    		obj.setName("小红");
            formatter.set(obj);
    	}
        System.out.println("ThreadLocal对象:"+formatter);
        System.out.println("Thread Name= "+name+" formatter = "+formatter.get());
        System.out.println();
    }
    
    
    static class TestObject {
    	private String name;

    	public String getName() {
    		return name;
    	}

    	public void setName(String name) {
    		this.name = name;
    	}
    }
}

运行结果如下:
java中ThreadLocal对象理解和使用
同运行结果可以看出:3个线程打印的ThreadLocal对象是同一个。而ThreadLocal中村拆除的TestObject对象,每个线程都是不同的对象。


简单了解ThreadLocal源码

通过set方法简单看下源码:
先通过当前线程获取ThreadLocalMap对象。如果没有,则创建ThreadLocalMap,将value存入。
java中ThreadLocal对象理解和使用
观察创建ThreadLocalMap对象,createMap方法会创建一个ThreadLocalMap对象,并把该对象赋值给当前线程的成员变量threadLocals。可以想得到,这个地方每个线程调用set时,t都是不同的。
java中ThreadLocal对象理解和使用
ThreadLocalMap的构造方法:会将ThreadLocal经过hash得到一个下标作为key,将value存储到ThreadLocalMap中的table中。这个地方需要注意的是,ThreadLocal<?> firstKey针对于所有线程,都是同一个对象。但是看上一步赋值的时候,每个线程调用的t都是不同的。
java中ThreadLocal对象理解和使用
每个线程持有一个ThreadLocalMap对象。每一个新的线程Thread都会实例化一个ThreadLocalMap并赋值给成员变量threadLocals,使用时若已经存在threadLocals则直接使用已经存在的对象。


借用ingxin大佬的总结:

1、对于某一ThreadLocal来讲,他的索引值i是确定的,在不同线程之间访问时访问的是不同的table数组的同一位置即都为table[i],只不过这个不同线程之间的table是独立的。
2、对于同一线程的不同ThreadLocal来讲,这些ThreadLocal实例共享一个table数组,然后每个ThreadLocal实例在table中的索引i是不同的。