zoukankan      html  css  js  c++  java
  • Java多线程之ThreadLocal

    ThreadLocal简介

    ThreadLocal是Java中的线程局部变量,用于存放线程的局部变量。

    ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,但是确避免线程同步所带来性能消耗,也减少了线程并发控制的复杂度。

    首先看一下ThreadLocal的API:

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

    可以看出ThreadLocal内部应该就是封装了一个Map,自己实现ThreadLocal:

    public class SimpleThreadLocal {
        private Map valueMap = Collections.synchronizedMap(new HashMap());
        public void set(Object newValue) {
            //①键为线程对象,值为本线程的变量副本
            valueMap.put(Thread.currentThread(), newValue);
        }
        public Object get() {
            Thread currentThread = Thread.currentThread();
    
            //②返回本线程对应的变量
            Object o = valueMap.get(currentThread); 
                    
            //③如果在Map中不存在,放到Map中保存起来
            if (o == null && !valueMap.containsKey(currentThread)) {
                o = initialValue();
                valueMap.put(currentThread, o);
            }
            return o;
        }
        public void remove() {
            valueMap.remove(Thread.currentThread());
        }
        public Object initialValue() {
            return null;
        }
    }

    以上代码很好理解,JDK中的实现比这复杂,可以自行查看源码。

    ThreadLocal基本使用

    ThreadLocal对象通常用于防止对可变的单实例变量或全局变量进行共享。

    当一个类中使用了static成员变量的时候,一定要多问问自己,这个static成员变量需要考虑线程安全吗?也就是说,多个线程需要独享自己的static成员变量吗?如果需要考虑,不妨使用ThreadLocal。

    例如,在单线程应用程序中可能会维护一个全局的数据库连接,并在程序启动的时候初始化这个连接,从而避免在调用每个方法的时候都要传递一个Connection对象。由于JDBC的连接对象不一定时线程安全的,因此,当多线程应用程序在没有协同的情况下使用全局变量时,就是不是线程安全的。通过把JDBC的连接保存到ThreadLocal对象中,每个线程都会拥有自己的连接。

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    
    public class ConnectionManager {
    
        private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
        @Override
        protected Connection initialValue() {
            Connection conn = null;
            try {
                conn = DriverManager.getConnection(
                        "jdbc:mysql://localhost:3306/test", "username",
                        "password");
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return conn;
        }
        };
    
        public static Connection getConnection() {
            return connectionHolder.get();
        }
    
        public static void setConnection(Connection conn) {
            connectionHolder.set(conn);
        }
    }

    ThreadLocal需要注意的点

    只要使用了“池”(线程池、连接池),在使用ThreadLocal时,尤其需要注意,每个线程在使用ThreadLocal的时候,必须对ThreadLocal执行一次clear操作,避免出现线程污染问题。

    线程池中的线程是重复利用的,只要线程还在,ThreadLocal线程本地变量会一直存在系统中,在JavaEE的服务器中尤为明显。

  • 相关阅读:
    关于android4.3 bluetooth4.0的那些事儿
    一个比较简单的夜间模式实现
    Android类装载器DexClassLoader的简单使用-----制作android插件的前奏
    Android学习笔记之 网络状态监听
    Android 学习笔记之 SQLite基础用法
    Android 学习笔记之 JSON的序列化基本用法
    Android学习笔记之 仿QQ登录界面的实现
    Android学习笔记之 生成KeyStore和给apk添加签名
    ArcGis for Android学习笔记之AsyncTask和IdentifyTask的应用
    Android学习笔记之 jar包的导入和异常处理
  • 原文地址:https://www.cnblogs.com/deityjian/p/11087141.html
Copyright © 2011-2022 走看看