zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    D. Babaei and Birthday Cake--- Codeforces Round #343 (Div. 2)
    Vijos P1389婚礼上的小杉
    AIM Tech Round (Div. 2) C. Graph and String
    HDU 5627Clarke and MST
    bzoj 3332 旧试题
    codeforces 842C Ilya And The Tree
    codesforces 671D Roads in Yusland
    Travelling
    codeforces 606C Sorting Railway Cars
    codeforces 651C Watchmen
  • 原文地址:https://www.cnblogs.com/shanyingwufeng/p/9942075.html
Copyright © 2011-2022 走看看