• java并发-ThreadLocal


    基本概念

    • ThreadLocal提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
    • 每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。

    实例

    public class ThreadLocalExample {
    
        public static class MyRunnable implements Runnable {
    
            private ThreadLocal<String> threadLocal = new ThreadLocal<>();
    
            @Override
            public void run() {
                threadLocal.set(Thread.currentThread().getName());
                try {
                Thread.sleep(2000);
                } catch (InterruptedException e) {
    
                }
                System.out.println(threadLocal.get());
            }
        }
    
        public static void main(String[] args) {
             MyRunnable sharedRunnableInstance = new MyRunnable();
             Thread thread1 = new Thread(sharedRunnableInstance);
             Thread thread2 = new Thread(sharedRunnableInstance);
             thread1.start();
             thread2.start();
        }
    
    }
    

    运行结果:

    Thread-0
    Thread-1
    

    使用ThreadLocal处理数据库连接:(代码来自网络)

    public class ConnectionManager {
    
    	/** 线程内共享Connection,ThreadLocal通常是全局的,支持泛型 */
    	private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
    	
    	public static Connection getCurrConnection() {
    		// 获取当前线程内共享的Connection
    		Connection conn = threadLocal.get();
    		try {
    			// 判断连接是否可用
    			if(conn == null || conn.isClosed()) {
    				// 创建新的Connection赋值给conn(略)
    				// 保存Connection
    				threadLocal.set(conn);
    			}
    		} catch (SQLException e) {
    			// 异常处理
    		}
    		return conn;
    	}
    	
    	/**
    	 * 关闭当前数据库连接
    	 */
    	public static void close() {
    		// 获取当前线程内共享的Connection
    		Connection conn = threadLocal.get();
    		try {
    			// 判断是否已经关闭
    			if(conn != null && !conn.isClosed()) {
    				// 关闭资源
    				conn.close();
    				// 移除Connection
    				threadLocal.remove();
    				conn = null;
    			}
    		} catch (SQLException e) {
    			// 异常处理
    		}
    	}
    }
    

    Hibernate中处理session

        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;  
        }  
    

    源码分析

    1:ThreadLocal中的方法
    T get() 返回此线程局部变量的当前线程副本中的值。
    protected T initialValue() 返回此线程局部变量的当前线程的“初始值”。
    void remove() 移除此线程局部变量当前线程的值。
    void set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值。

    public void set(T value) {  
        // 获取当前线程对象  
        Thread t = Thread.currentThread();  
        // 获取当前线程本地变量Map  
        ThreadLocalMap map = getMap(t);  
        // map不为空  
        if (map != null)  
            // 存值  
            map.set(this, value);  
        else  
            // 创建一个当前线程本地变量Map  
            createMap(t, value);  
    }  
      
    /** 
     * Get the map associated with a ThreadLocal. Overridden in 
     * InheritableThreadLocal. 
     * 
     * @param  t the current thread 
     * @return the map 
     */  
    ThreadLocalMap getMap(Thread t) {  
        // 获取当前线程的本地变量Map  
        return t.threadLocals;  
    }  
    

    ThreadLocal中是有一个Map,ThreadLocalMap,ThreadLocalMap是ThreadLocal的一个内部类。当使用ThreadLocal存值时,首先是获取到当前线程对象,然后获取到当前线程本地变量Map,最后将当前使用的ThreadLocal和传入的值放到Map中,这样做的好处是,每个线程都对应一个本地变量的Map,所以一个线程可以存在多个线程本地变量。

    get()

    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(t)方法获取到一个map,map的类型为ThreadLocalMap。然后接着下面获取到<key,value>键值对,注意这里获取键值对传进去的是 this,而不是当前线程t。如果获取成功,则返回value值。如果map为空,则调用setInitialValue方法返回value。

    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;
        }
    

    setInitialValue()方法我们可以看出,map不为空,就设置键值对,为空,再创建Map

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

    我们可以看出创建map是线程中调用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;
                }
            }
    
    public void remove() {
             ThreadLocalMap m = getMap(Thread.currentThread());
             if (m != null)
                 m.remove(this);
         }
    
    

    移除此线程局部变量当前线程的值。如果此线程局部变量随后被当前线程读取,且这期间当前线程没有设置其值,则将调用其 initialValue() 方法重新初始化其值。这将导致在当前线程多次调用 initialValue 方法。

  • 相关阅读:
    springboot、监听器
    springboot、拦截器
    Thymeleaf模板引擎
    springboot-banner.txt
    springboot,swagger2
    springboot 热部署
    判断是否为微信环境下打开的网页
    后台接收json数据
    ios 面试题
    iOS 适配问题
  • 原文地址:https://www.cnblogs.com/csuwater/p/5414488.html
Copyright © 2020-2023  润新知