ThreadLocal的浅析

  • 引入

先来看下面一段代码

package pack01;

import java.util.HashMap;
import java.util.Map;

class MyThread implements Runnable{
	private Map<Thread,String> map;
	
	public MyThread(Map<Thread, String> map) {
		this.map = map;
	}

	@Override
	public void run() {
		String string = map.get(Thread.currentThread());
		System.out.println("string:"+string);
	}
}

public class Test {
	public static void main(String[] args) {
		Map<Thread,String> map = new HashMap<>();
		map.put(Thread.currentThread(),"我是main线程");
		
		Thread t = new Thread(new MyThread(map));
		t.start();
	}
}

当程序运行的时候,main线程向map中存入了数据"我是main线程",当想要通过t线程去读取map中的数据时,发现结果是string:null;

这是因为map的键是当前线程对象,所以只有通过本线程才能找到对应的值,这一点和ThreadLocal的原理很像.

  • 概述

java.lang.ThreadLocal;

public class ThreadLocal<T>
extends Object

ThreadLocal不是一个Thread,更确切的说,它是一个线程局部变量,它为每一个使用该变量的线程提供一个独立于该变量的副本,每个线程可以独立的使用自己的副本,该副本与当前线程绑定.通过ThreadLocal操作的数据也都是与本线程相关的,多线程并发时,ThreadLocal为每一个线程都提供了一份变量,解决多线程资源共享的问题.

ThreadLocal有一个静态内部类static class ThreadLocalMap,它的键就是使用该变量的线程对象,值是Object类型的变量(本类的其它任何变量均可)

  • 方法

构造方法:   ThreadLocal()  创建一个线程本地变量

成员方法:

1. T get()

返回此线程局部变量的当前线程副本中的值。如果变量没有用于当前线程的值,则先将其初始化为调用 initialValue() 方法返回的值。 

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
 
 2. void set(T value) 

将此线程局部变量的当前线程副本中的值设置为指定值。大部分子类不需要重写此方法,它们只依靠initialValue()方法来设置线程局部变量的值。 

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
 3. void remove()

移除此线程局部变量当前线程的值。

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


返回此线程局部变量的当前线程的“初始值”。线程第一次使用 get()方法访问变量时将调用此方法,但如果线程之前调用了 set(T)方法,则不会对该线程再调用initialValue 方法。通常,此方法对每个线程最多调用一次,但如果在调用 get() 后又调用了 remove(),则可能再次调用此方法。

该实现返回 null;如果程序员希望线程局部变量具有 null 以外的值,则必须为 ThreadLocal 创建子类,并重写此方法。通常将使用匿名内部类完成此操作。

protected T initialValue() {
        return null;
    }
以上代码 部分来自JDK1.7源码

4.ThreadLocalMap

ThreadLocal中的静态内部类,也是ThreadLocal类的关键,里面存放了一个大小为16的数组,数组元素是Entry对象(静态内部类),

Entry的数据结构是key-value形式的,键就是使用该变量的线程对象

static class Entry extends WeakReference<ThreadLocal> {
            Object value;
            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
 当使用ThreadLocal保存一个变量的时候,就会在ThreadLocalMap中插入一个Entry对象,因为Entry继承了WeakReference类,属于弱引用类型

如果创建ThreadLocal的线程一致运行,Entry中的key因为是弱引用类型可以被GC回收,value却得不到回收,会产生内存泄漏,此类问题在

ThreadLocalMap的get()和set()方法中,会判断如果key为null,就会清除对应的value

以上代码部分来自JDK1.7源码

5.应用举例

public class ConnectionManager {
	//1: 创建一个ThreadLocal对象
	private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
	public static Connection getConnection() throws SQLException{
	  //1:如果是第一次线程调用该方法,则存入一个Connection对象,并将对象返回
		//先尝试获取一次,如果返回的是null,就表示第一次
		Connection conn = tl.get();//哪个线程来调用该方法,这里的键就是该线程对象
		if(conn == null){
			conn = MyC3P0Utils.getConnection();
			tl.set(conn);
		}
		return conn;
	}
}
这是一个管理连接的工具类,当调用此类的getConnection()方法时,希望同一线程每次获取的连接总是同一个,就使用到ThreadLocal


参考文档:JDK官方文档






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值