ThreadLocal

1.ThreadLocal源码解读

      (1)get()

                get()方法是用来获取ThreadLocal在当期线程中保存的变量副本。

    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();
    }
               再看一下getMap方法中的操作

    ThreadLocalMap getMap(Thread t) 
    {
        return t.threadLocals;
    }
          可以看出,实际上是从当前线程t中获得成员变量threadLocals。那么再看一下Thread类中的threadLocals成员变量

    ThreadLocal.ThreadLocalMap threadLocals = null;
         发现其实也就是一个ThreadLocal类中定义的一个内部类ThreadLocalMap类的对象,继续看ThreadLocalMap的实现:

    static class ThreadLocalMap 
    {
        static class Entry extends WeakReference<ThreadLocal> 
        {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v)
            {
                super(k);
                value = v;
            }
        }
.......
    }
       那么就可以看出,实际上ThreadLocalMap也是一个Map,将ThreadLocal作为key

       继续看get()中的setInitialValue()方法

    private T setInitialValue() 
   {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
         可以看出如果map不为空,则直接将this作为key存储到ThreadLocalMap中,否则则创建Map,creteMap方法为:

    void createMap(Thread t, T firstValue) 
   {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
       也就是说如果map为空,则直接创建一个即可


      (2)set(T value)

                set(T value)方法是用来设置当期线程中变量的副本。

    public void set(T value) 
   {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
            可以看出,也就是在当前线程的Thread对象中的ThreadLocalMap中设置对应的值


2.ThreadLocal应用

       看一个例子

public class ThreadLocalTest 
{
	public static void main(String[] args) throws InterruptedException 
	{
		final Local local = new Local();
		local.set(1,"main");
		
		new Thread(new Runnable() 
		{
			@Override
			public void run() 
			{
				local.set(2,"sub");
				local.show();
			}
		}).start();
		
		Thread.sleep(1000);
		local.show();
	}
}

class Local
{
	private ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
	private ThreadLocal<String> strLocal = new ThreadLocal<String>();
	
	public void set(long l,String n)
	{
		longLocal.set(l);
		strLocal.set(n);
	}
	
	public void show()
	{
		System.out.println(longLocal.get());
		System.out.println(strLocal.get());
	}
}
               结果为:

2
sub
1
main

              可以看出,每个线程都有自己的值,说明ThreadLocal起作用了


3.ThreadLocal理解

       由于是在Thread类中定义一个Map,所以必然就能不同线程不同值


4.ThreadLocal应用场景

      假设有一个数据库连接类,维护了一个数据库连接:
class ConnectionManager 
{
     
    private static Connection connect = null;
     
    public static Connection openConnection() 
    {
        if(connect == null)
        {
            connect = DriverManager.getConnection();
        }
        return connect;
    }
     
    public static void closeConnection()
    {
        if(connect!=null)
            connect.close();
    }
}
           在单线程程序中,这段代码没有任何问题,但是放在多线程情况下,会存在以下两个问题:1.两个方法都没有进行同步,可能会在openConnection中多次创建connect   2.由于connect是共享变量,所以所有线程都是用这一个变量,那么一个线程在是用的时候,其他线程很有可能在closeConnextion关闭连接,从而导致问题
           所以处于安全考虑,必须将这两个方法进行同步处理。
           但是由于所有线程共享一个connect,所以当一个线程在进行同步操作的时候,其他线程只能等待,这样会使效率非常低
           实际上,数据库连接完全可以不用共享,那么直接改为在方法中调用如何呢?
class ConnectionManager {
     
    private  Connection connect = null;
     
    public Connection openConnection() {
        if(connect == null){
            connect = DriverManager.getConnection();
        }
        return connect;
    }
     
    public void closeConnection() {
        if(connect!=null)
            connect.close();
    }
}
 
 
class Dao{
    public void insert() {
        ConnectionManager connectionManager = new ConnectionManager();
        Connection connection = connectionManager.openConnection();
         
        //使用connection进行操作
         
        connectionManager.closeConnection();
    }
}
              这样做确实没有问题,但是由于每次都是在方法内部创建连接,那么就会导致服务器压力非常大,严重影响程序性能。
              这种情况最适合使用ThreadLocal,因为ThreadLocal会对每一个线程内部构造一个副本,并且在线程内部任何地方都可以使用,这样一来就不存在线程安全问题,也不会严重影响程序性能。
              ThreadLocal最适合这种每个线程都需要保存自己副本的情况,比如数据库连接池,Session等
private static ThreadLocal<Connection> connectionHolder  = new ThreadLocal<Connection>()
 {
public Connection initialValue()
 {
    return DriverManager.getConnection(DB_URL);
}
};
 
public static Connection getConnection() 
{
return connectionHolder.get();
}
                重写了initialValue方法,也就是在线程初始化的时候创建一个数据库连接

private static final ThreadLocal threadSession = new ThreadLocal();
 
public static Session getSession() throws InfrastructureException 
{
    Session s = (Session) threadSession.get();
    try 
    {
        if (s == null)
        {
            s = getSessionFactory().openSession();
            threadSession.set(s);
        }
    } 
    catch (HibernateException ex)
    {
        throw new InfrastructureException(ex);
    }
    return s;
}
                 用来管理Session

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值