• Java并发编程-ThreadLocal


    一、ThreadLocal 的理解

    ThreadLocal 由 JDK 包提供,它提供了线程本地变量,如果创建了一个 ThreadLocal 变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。当多个线程操作这个变量的时候,实际上是操作自己本地内存里面的变量,从而避免了线程安全问题。创建了一个 ThreadLocal 变量后,每个线程都会复制一个变量到自己的本地内存中。如图所示:

    ThreadLocal01.png

    二、ThreadLocal 的示例

    代码如下:

    public class ThreadLocalTest {
    	
    	// 创建ThreadLocal变量
    	public static ThreadLocal<String> localVariable = new ThreadLocal<>();
    	
    	// 打印方法
    	public static void print(String str) {
    		// 打印当前线程本地内存中localVariable变量的值
    		System.out.println(str + ":" + localVariable.get());
    		// 清除当前线程本地内存中localVariable变量的值
    		localVariable.remove();
    	}
    	
    	public static void main(String[] args) {
    		
    		// 创建线程thread1
    		Thread thread1 = new Thread(new Runnable() {
    			
    			@Override
    			public void run() {
    				// TODO Auto-generated method stub
    				// 设置线程thread1中本地变量localVariable的值
    				localVariable.set("thread1 local variable");
    				print("thread1");
    				System.out.println("thread1 remove after" + ":" + localVariable.get());	
    			}
    		});
    		
    		// 创建线程thread2
    		Thread thread2 = new Thread(new Runnable() {
    			
    			@Override
    			public void run() {
    				// TODO Auto-generated method stub
    				// 设置线程thread2中本地变量localVariable的值
    				localVariable.set("thread2 local variable");
    				print("thread2");
    				System.out.println("thread2 remove after" + ":" + localVariable.get());
    			}
    		});
    		
    		// 启动线程
    		thread1.start();
    		thread2.start();
    		
    	}
    
    }
    

    运行结果如下:

    thread1:thread1 local variable
    thread2:thread2 local variable
    thread1 remove after:null
    thread2 remove after:null
    

    将 print() 方法中的 localVariable.remove() 注释,再次运行得到运行结果:

    thread1:thread1 local variable
    thread2:thread2 local variable
    thread1 remove after:thread1 local variable
    thread2 remove after:thread2 local variable
    

    三、ThreadLocal 的原理

    ThreadLocal 类提供的几种方法:

    public void set(T value) { }
    public T get() { }
    public void remove() { }
    

    1、public void set(T value) { }

    public void set(T value) {
        // (1)获取当前线程
        Thread t = Thread.currentThread();
        // (2)将当前线程作为key,查找对应的线程变量,如果找到则设置
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            // (3)如果第一次调用则创建当前线程对应的HashMap
            createMap(t, value);
    }
    

    代码(1)首先获取当前的使用线程,然后使用其作为参数传入 getMap(t) 方法,getMap(Thread t) 的代码如下:

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    

    可以看出,getMap(t) 的目的是获取当前线程自己的 threadLocals 变量。

    如果 getMap(t) 的返回值不为空,则把 value 值设置到 threadLocals 中,也就是把当前变量值放入当前线程的内存变量 threadLocals 中。其中 this 代表 ThreadLocal 的实例对象引用,value 是通过 set 方法传递的值。

    如果 getMap(t) 的返回值为空,则说明是第一次调用 set 方法,这时候会创建当前线程的 threadLocals 变量。createMap(t, value):

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    

    可以看出,createMap(t, value) 方法创建当前线程的 threadLocals 变量。

    2、T get() { }

    public T get() {
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取当前线程的threadLocals变量
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    

    首先获取当前线程实例,如果当前线程的 threadLocals 变量不为 null,则直接返回当前线程绑定的本地变量,否则进行初始化。

    3、void remove() { }

    public void remove() {
        ThreadLocalMap m = getMap(Thread.currentThread());
        if (m != null)
            m.remove(this);
    }
    

    如果当前线程的 threadLocals 变量不为空,则删除当前线程中指定 ThreadLocal 实例的本地变量。

    总结:每个线程内部都有一个名为 threadLocals 的成员变量,该变量的类型 HashMap,其中 key 为我们定义的 ThreadLocal 变量的 this 引用,value 则为我们使用 set 方法设置的值。每个线程的本地变量存放在线程自己的内存变量 threadLocals 中,如果当前线程一直不消亡,则这些本地变量会一直存在,所以可能会造成内存溢出,因此使用完毕后需要调用 ThreadLocal 的 remove 方法删除对应线程的 threadLocals 中的本地变量。如图所示:

    ThreadLocal-zongjie.png

  • 相关阅读:
    Windows Driver Mode 1
    一个程序员的奋斗经历 2
    JavaScript判断文件是否存在
    流程图个图标详解
    wget for windows
    软件的开发周期
    Firefox支持activex的插件
    第二次作业
    C/C++字符串使用整理
    c#学习小记录(1)
  • 原文地址:https://www.cnblogs.com/shanyingwufeng/p/9942075.html
Copyright © 2020-2023  润新知