多线程 ThreadLocal

要了解ThreadLocal,首先搞清楚ThreadLocal 是什么?是用来解决什么问题的?

ThreadLocal 是线程的局部变量, 是每一个线程所单独持有的,其他线程不能对其进行访问, 通常是类中的 private static 字段,是对该字段初始值的一个拷贝,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联

我们知道有时候一个对象的变量会被多个线程所访问,这时就会有线程安全问题,当然我们可以使用synchorinized 关键字来为此变量加锁,进行同步处理,从而限制只能有一个线程来使用此变量,但是加锁会大大影响程序执行效率,此外我们还可以使用ThreadLocal来解决对某一个变量的访问冲突问题。

当使用ThreadLocal维护变量的时候 为每一个使用该变量的线程提供一个独立的变量副本,即每个线程内部都会有一个该变量,这样同时多个线程访问该变量并不会彼此相互影响,因此他们使用的都是自己从内存中拷贝过来的变量的副本, 这样就不存在线程安全问题,也不会影响程序的执行性能。

但是要注意,虽然ThreadLocal能够解决上面说的问题,但是由于在每个线程中都创建了副本,所以要考虑它对资源的消耗,比如内存的占用会比不使用ThreadLocal要大。

ThreadLocal 方法使用详解

ThreadLocal 的几个方法: ThreadLocal 可以存储任何类型的变量对象, get返回的是一个Object对象,但是我们可以通过泛型来制定存储对象的类型。

public T get() { } // 用来获取ThreadLocal在当前线程中保存的变量副本
public void set(T value) { } //set()用来设置当前线程中变量的副本 public void remove() { } //remove()用来移除当前线程中变量的副本 protected T initialValue() { } //initialValue()是一个protected方法,一般是用来在使用时进行重写的

Thread 在内部是通过ThreadLocalMap来维护ThreadLocal变量表, 在Thread类中有一个threadLocals 变量,是ThreadLocalMap类型的,它就是为每一个线程来存储自身的ThreadLocal变量的, ThreadLocalMap是ThreadLocal类的一个内部类,这个Map里面的最小的存储单位是一个Entry, 它使用ThreadLocal作为key, 变量作为 value,这是因为在每一个线程里面,可能存在着多个ThreadLocal变量

初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。 
然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找

ThreadLocal是如何为每一个线程创建一个变量副本的,下面举一个例子来看一看。例子来源于 博客http://www.cnblogs.com/dolphin0520/p/3920407.html

public class ThreadLocalTest {
    public static void main(String[] args) throws InterruptedException { final ThreadLocalTest test = new ThreadLocalTest(); test.set(); System.out.println(test.getLong()); System.out.println(test.getString()); // 在这里新建了一个线程 Thread thread1 = new Thread() { public void run() { test.set(); // 当这里调用了set方法,进一步调用了ThreadLocal的set方法是,会将ThreadLocal变量存储到该线程的ThreadLocalMap类型的成员变量threadLocals中,注意的是这个threadLocals变量是Thread线程的一个变量,而不是ThreadLocal类的变量。 System.out.println(test.getLong()); System.out.println(test.getString()); }; }; thread1.start(); thread1.join(); System.out.println(test.getLong()); System.out.println(test.getString()); } ThreadLocal<Long> longLocal = new ThreadLocal<Long>(); ThreadLocal<String> stringLocal = new ThreadLocal<String>(); public void set() { longLocal.set(Thread.currentThread().getId()); stringLocal.set(Thread.currentThread().getName()); } public long getLong() { return longLocal.get(); } public String getString() { return stringLocal.get(); } }

代码的输出结果: 

main 

Thread-0 

main

ThreadLocal 的应用场景

最常见的ThreadLocal使用场景为 
用来解决 数据库连接、Session管理等。

数据库连接:

Class A implements Runnable{ private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { public Connection initialValue() { return DriverManager.getConnection(DB_URL); } }; public static Connection getConnection() { return connectionHolder.get(); } }

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

猜你喜欢

转载自www.cnblogs.com/jtlgb/p/9485773.html