ThreadLocal的理解

前言:

知其然知其所以然,所以先来说一下ThreadLocal是为了什么用途而应运而生的。

作用:

ThreadLocal实现了线程的数据隔离,提供了属于线程的独享局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作。

用法:

对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
1、ThreadLocal.get:
获取ThreadLocal中当前线程共享变量的值。
2、ThreadLocal.set:
设置ThreadLocal中当前线程共享变量的值。
3、ThreadLocal.remove:
移除ThreadLocal中当前线程共享变量的值。
4、ThreadLocal.initialValue:
ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值。

典型应用场景:

1.管理Connection
数据库连接池的connection让我们交由ThreadLocal来进行管理。为什么交给它来管理呢??ThreadLocal能够实现当前线程的操作都是用同一个Connection,保证了事务!

public static Connection getConnection() throws SQLException { 
//获取Connection对象 
Connection connection = source.getConnection();
 //把Connection放进ThreadLocal里面 
local.set(connection); 
//返回Connection对象 
return connection; } 
//关闭数据库连接
 public static void closeConnection() { 
//从线程中拿到Connection对象 
Connection connection = local.get();
 try { if (connection != null) {
 //恢复连接为自动提交
 connection.setAutoCommit(true); 
//这里不是真的把连接关了,只是将该连接归还给连接池
 connection.close(); 
//既然连接已经归还给连接池了,ThreadLocal保存的Connction对象也已经没用了
 local.remove(); }
 } catch (SQLException e) { e.printStackTrace(); } } }

实现的原理

Local.set();
public void set(T value) { 
// 得到当前线程对象
 Thread t = Thread.currentThread();
 // 这里获取ThreadLocalMap 
ThreadLocalMap map = getMap(t);
 // 如果map存在,则将当前线程对象t作为key,要存储的对象作为value存到map里面去
 if (map != null) map.set(this, value); else createMap(t, value); }

ThreadLocalMap是ThreadLocal的一个内部类。用Entry类来进行存储
我们的值都是存储到这个Map上的,key是当前ThreadLocal对象!

public T get() { 
Thread t = Thread.currentThread();
 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(); 
}

ThreadLocal原理总结

  • 每个Thread维护着一个ThreadLocalMap的引用
  • ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
  • 调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象
  • 调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象
    ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。

对象关系引用图

对象关系引用图

内存泄露:

ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,想要避免内存泄露就要手动remove()掉!

代码示例
package threadLocal;

/**
 * ThreadLocal用法
 * @author coshaho
 *
 */
public class MyThreadLocal
{
    private static final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>(){
        /**
         * ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值
         */
        @Override
        protected Object initialValue()
        {
            System.out.println("调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!");
            return null;
        }
    };

    public static void main(String[] args)
    {
        new Thread(new MyIntegerTask("IntegerTask1")).start();
        new Thread(new MyStringTask("StringTask1")).start();
        new Thread(new MyIntegerTask("IntegerTask2")).start();
        new Thread(new MyStringTask("StringTask2")).start();
    }


    public static class MyIntegerTask implements Runnable
    {
        private String name;

        MyIntegerTask(String name)
        {
            this.name = name;
        }
        @Override
        public void run()
        {
            for(int i = 0; i < 5; i++)
            {
                // ThreadLocal.get方法获取线程变量
                if(null == MyThreadLocal.threadLocal.get())
                {
                    // ThreadLocal.set方法设置线程变量
                    MyThreadLocal.threadLocal.set(0);
                    System.out.println("线程" + name + ": 0");
                }
                else
                {
                    int num = (Integer)MyThreadLocal.threadLocal.get();
                    MyThreadLocal.threadLocal.set(num + 1);
                    System.out.println("线程" + name + ": " + MyThreadLocal.threadLocal.get());
                    if(i == 3)
                    {
                        MyThreadLocal.threadLocal.remove();
                    }
                }
                try
                {
                    Thread.sleep(1000);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }  
        }

    }

    public static class MyStringTask implements Runnable
    {
        private String name;

         MyStringTask(String name)
        {
            this.name = name;
        }

        @Override
        public void run()
        {
            for(int i = 0; i < 5; i++)
            {
                if(null == MyThreadLocal.threadLocal.get())
                {
                    MyThreadLocal.threadLocal.set("a");
                    System.out.println("线程" + name + ": a");
                }
                else
                {
                    String str = (String)MyThreadLocal.threadLocal.get();
                    MyThreadLocal.threadLocal.set(str + "a");
                    System.out.println("线程" + name + ": " + MyThreadLocal.threadLocal.get());
                }
                try
                {
                    Thread.sleep(800);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }

    }
}

输出结果

调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask1: 0
线程StringTask1: a
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程StringTask2: a
线程IntegerTask2: 0
线程StringTask1: aa
线程StringTask2: aa
线程IntegerTask2: 1
线程IntegerTask1: 1
线程StringTask1: aaa
线程StringTask2: aaa
线程IntegerTask1: 2
线程IntegerTask2: 2
线程StringTask1: aaaa
线程StringTask2: aaaa
线程IntegerTask2: 3
线程IntegerTask1: 3
线程StringTask1: aaaaa
线程StringTask2: aaaaa
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask2: 0
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask1: 0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值