• 技术文章 >Java >java教程

    Java中ThreadLocal的详细介绍(代码示例)

    不言不言2019-03-06 15:56:28转载1106
    本篇文章给大家带来的内容是关于Java中ThreadLocal的详细介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

    ThreadLocal基本在项目开发中基本不会用到, 但是面试官是比较喜欢问这类问题的;所以还是有必要了解一下该类的功能与原理的.

    ThreadLocal是什么

    ThreadLocal是一个将在多线程中为每一个线程创建单独的变量副本的类; 当使用ThreadLocal来维护变量时, ThreadLocal会为每个线程创建单独的变量副本, 避免因多线程操作共享变量而导致的数据不一致的情况;

    ThreadLocal类用在哪些场景

    一般来说, ThreadLocal在实际工业生产中并不常见, 但是在很多框架中使用却能够解决一些框架问题; 比如Spring中的事务、Spring 中 作用域 ScopeRequest的Bean 使用ThreadLocal来解决.

    ThreadLocal使用方法

    1、将需要被多线程访问的属性使用ThreadLocal变量来定义; 下面以网上多数举例的DBConnectionFactory类为例来举例

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    
    public class DBConnectionFactory {
    
        private static final ThreadLocal<Connection> dbConnectionLocal = new ThreadLocal<Connection>() {
            @Override
            protected Connection initialValue() {
                try {
                    return DriverManager.getConnection("", "", "");
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                return null;
            }
        };
    
        public Connection getConnection() {
            return dbConnectionLocal.get();
        }
    }

    这样在Client获取Connection的时候, 每个线程获取到的Connection都是该线程独有的, 做到Connection的线程隔离; 所以并不存在线程安全问题

    ThreadLocal如何实现线程隔离

    1、主要是用到了Thread对象中的一个ThreadLocalMap类型的变量threadLocals, 负责存储当前线程的关于Connection的对象, 以dbConnectionLocal 这个变量为Key, 以新建的Connection对象为Value; 这样的话, 线程第一次读取的时候如果不存在就会调用ThreadLocal的initialValue方法创建一个Connection对象并且返回;

    具体关于为线程分配变量副本的代码如下:

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

    1、首先获取当前线程对象t, 然后从线程t中获取到ThreadLocalMap的成员属性threadLocals

    2、如果当前线程的threadLocals已经初始化(即不为null) 并且存在以当前ThreadLocal对象为Key的值, 则直接返回当前线程要获取的对象(本例中为Connection);

    3、如果当前线程的threadLocals已经初始化(即不为null)但是不存在以当前ThreadLocal对象为Key的的对象, 那么重新创建一个Connection对象, 并且添加到当前线程的threadLocals Map中,并返回

    4、如果当前线程的threadLocals属性还没有被初始化, 则重新创建一个ThreadLocalMap对象, 并且创建一个Connection对象并添加到ThreadLocalMap对象中并返回。

    如果存在则直接返回很好理解, 那么对于如何初始化的代码又是怎样的呢?

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

    1、首先调用我们上面写的重载过后的initialValue方法, 产生一个Connection对象

    2、继续查看当前线程的threadLocals是不是空的, 如果ThreadLocalMap已被初始化, 那么直接将产生的对象添加到ThreadLocalMap中, 如果没有初始化, 则创建并添加对象到其中;

    同时, ThreadLocal还提供了直接操作Thread对象中的threadLocals的方法

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

    这样我们也可以不实现initialValue, 将初始化工作放到DBConnectionFactory的getConnection方法中:

    public Connection getConnection() {
        Connection connection = dbConnectionLocal.get();
        if (connection == null) {
            try {
                connection = DriverManager.getConnection("", "", "");
                dbConnectionLocal.set(connection);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return connection;
    }

    那么我们看过代码之后就很清晰的知道了为什么ThreadLocal能够实现变量的多线程隔离了; 其实就是用了Map的数据结构给当前线程缓存了, 要使用的时候就从本线程的threadLocals对象中获取就可以了, key就是当前线程;

    当然了在当前线程下获取当前线程里面的Map里面的对象并操作肯定没有线程并发问题了, 当然能做到变量的线程间隔离了;

    现在我们知道了ThreadLocal到底是什么了, 又知道了如何使用ThreadLocal以及其基本实现原理了是不是就可以结束了呢? 其实还有一个问题就是ThreadLocalMap是个什么对象, 为什么要用这个对象呢?

    ThreadLocalMap对象是什么

    本质上来讲, 它就是一个Map, 但是这个ThreadLocalMap与我们平时见到的Map有点不一样

    1、它没有实现Map接口;

    2、它没有public的方法, 最多有一个default的构造方法, 因为这个ThreadLocalMap的方法仅仅在ThreadLocal类中调用, 属于静态内部类

    3、ThreadLocalMap的Entry实现继承了WeakReference<ThreadLocal<?>>

    4、该方法仅仅用了一个Entry数组来存储Key, Value; Entry并不是链表形式, 而是每个bucket里面仅仅放一个Entry;

    以上就是Java中ThreadLocal的详细介绍(代码示例)的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:segmentfault,如有侵犯,请联系admin@php.cn删除
    专题推荐:java
    上一篇:spring cloud gateway全局过滤器的介绍 下一篇:JSP和ASP之间的区别是什么?
    大前端线上培训班

    相关文章推荐

    • Java多线程中有关InheritableThreadLocal的详解• ThreadLocal是什么?ThreadLocal的原理分析• Java多线程之ThreadLocal的使用• ThreadLocal的实现原理的分析介绍(附代码)

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网