并发和多线程(十二)--ThreadLocal

ThreadLocal是什么?

  当使用ThreadLocal修饰变量的时候,ThreadLocal会为每个使用该变量的线程提供独立的变量副本,每个线程可以独立改变自己的副本,而不

影响其他线程的变量副本。

  相对于synchronized和lock实现对共享资源的操作互斥而实现原子性,这是一种新的思路解决并发问题。

原理:

public class Thread implements Runnable {
    ThreadLocal.ThreadLocalMap threadLocals = null;
}
static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        private static final int INITIAL_CAPACITY = 16;

        private Entry[] table;

        private int size = 0;

        private int threshold; // Default to 0

        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }

        private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }

        private static int prevIndex(int i, int len) {
            return ((i - 1 >= 0) ? i - 1 : len - 1);
        }
}

ThreadLocalMap:

  ThreadLocal的内部类,类似Hashmap结构,以ThreadLocal为key,需要隔离的数据为value的Entry键值对数组结构。

  Entry继承了WeakReferences,只要发生GC,key为null的entry就会被清理掉

get()源码:

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);//获取当前线程的ThreadLocalMap
    if (map != null) {//如果不为空
        ThreadLocalMap.Entry e = map.getEntry(this);//取出对应位置的Entry
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;//取出当前ThreadLocal对应的value值,返回
            return result;
        }
    }
    return setInitialValue();//如果没取到,进行初始化
}

getMap()源码:

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;//获取线程的ThreadLocals,也就是ThreadLocal.ThreadLocalMap
}

setInitialValue()源码:

private T setInitialValue() {
    T value = initialValue();//自定义初始化
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)//有对应的map,直接set
        map.set(this, value);
    else                   //否则创建新的map,保存当前线程内部
        createMap(t, value);
    return value;
}

set()源码:和前面一样

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

remove()源码:

public void remove() {
     ThreadLocalMap m = getMap(Thread.currentThread());
     if (m != null)
         m.remove(this);
 }
private void remove(ThreadLocal<?> key) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { if (e.get() == key) { e.clear(); expungeStaleEntry(i); return; } } }

应用:

  数据库连接、Session管理、用户管理

public class UserContext {
    
    private static ThreadLocal<User> userHolder = new ThreadLocal<User>();
    
    public static void setUser(User user) {
        userHolder.set(user);
    }
    
    public static User getUser() {
        return userHolder.get();
    }

}

下面两段代码来自:https://www.cnblogs.com/dolphin0520/p/3920407.html

public static ThreadLocal<Connection> connectionHolder = ThreadLocal.withInitial(() -> {
Connection conn = null;
try {
  conn = DriverManager.getConnection("", "", "");
} catch (SQLException e) {
  e.printStackTrace();
}
  return conn;
});

public static Connection getConnection() {
  return connectionHolder.get();
}
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;
}

与Thread同步机制的比较:

  ThreadLocal:用于线程间的数据隔离,适用于多实例对象的访问,并且这个对象很多地方都要用到

  Synchronized:用于线程间的数据共享

Spring中的应用:

  只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(

如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让

它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。

 

原文地址:https://www.cnblogs.com/huigelaile/p/10844991.html