ThreadLocal

来源:互联网 发布:自动刷火车票软件 编辑:程序博客网 时间:2024/06/08 06:34

1.ThreadLocal源码解读

      (1)get()

                get()方法是用来获取ThreadLocal在当期线程中保存的变量副本。

    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方法中的操作

    ThreadLocalMap getMap(Thread t)     {        return t.threadLocals;    }
          可以看出,实际上是从当前线程t中获得成员变量threadLocals。那么再看一下Thread类中的threadLocals成员变量

    ThreadLocal.ThreadLocalMap threadLocals = null;
         发现其实也就是一个ThreadLocal类中定义的一个内部类ThreadLocalMap类的对象,继续看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;            }        }.......    }
       那么就可以看出,实际上ThreadLocalMap也是一个Map,将ThreadLocal作为key

       继续看get()中的setInitialValue()方法

    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;    }
         可以看出如果map不为空,则直接将this作为key存储到ThreadLocalMap中,否则则创建Map,creteMap方法为:

    void createMap(Thread t, T firstValue)    {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }
       也就是说如果map为空,则直接创建一个即可


      (2)set(T value)

                set(T value)方法是用来设置当期线程中变量的副本。

    public void set(T value)    {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }
            可以看出,也就是在当前线程的Thread对象中的ThreadLocalMap中设置对应的值


2.ThreadLocal应用

       看一个例子

public class ThreadLocalTest {public static void main(String[] args) throws InterruptedException {final Local local = new Local();local.set(1,"main");new Thread(new Runnable() {@Overridepublic void run() {local.set(2,"sub");local.show();}}).start();Thread.sleep(1000);local.show();}}class Local{private ThreadLocal<Long> longLocal = new ThreadLocal<Long>();private ThreadLocal<String> strLocal = new ThreadLocal<String>();public void set(long l,String n){longLocal.set(l);strLocal.set(n);}public void show(){System.out.println(longLocal.get());System.out.println(strLocal.get());}}

               结果为:

2sub1main

              可以看出,每个线程都有自己的值,说明ThreadLocal起作用了


3.ThreadLocal理解

       由于是在Thread类中定义一个Map,所以必然就能不同线程不同值


4.ThreadLocal应用场景

      假设有一个数据库连接类,维护了一个数据库连接:
class ConnectionManager {         private static Connection connect = null;         public static Connection openConnection()     {        if(connect == null)        {            connect = DriverManager.getConnection();        }        return connect;    }         public static void closeConnection()    {        if(connect!=null)            connect.close();    }}
           在单线程程序中,这段代码没有任何问题,但是放在多线程情况下,会存在以下两个问题:1.两个方法都没有进行同步,可能会在openConnection中多次创建connect   2.由于connect是共享变量,所以所有线程都是用这一个变量,那么一个线程在是用的时候,其他线程很有可能在closeConnextion关闭连接,从而导致问题
           所以处于安全考虑,必须将这两个方法进行同步处理。
           但是由于所有线程共享一个connect,所以当一个线程在进行同步操作的时候,其他线程只能等待,这样会使效率非常低
           实际上,数据库连接完全可以不用共享,那么直接改为在方法中调用如何呢?
class ConnectionManager {         private  Connection connect = null;         public Connection openConnection() {        if(connect == null){            connect = DriverManager.getConnection();        }        return connect;    }         public void closeConnection() {        if(connect!=null)            connect.close();    }}  class Dao{    public void insert() {        ConnectionManager connectionManager = new ConnectionManager();        Connection connection = connectionManager.openConnection();                 //使用connection进行操作                 connectionManager.closeConnection();    }}
              这样做确实没有问题,但是由于每次都是在方法内部创建连接,那么就会导致服务器压力非常大,严重影响程序性能。
              这种情况最适合使用ThreadLocal,因为ThreadLocal会对每一个线程内部构造一个副本,并且在线程内部任何地方都可以使用,这样一来就不存在线程安全问题,也不会严重影响程序性能。
              ThreadLocal最适合这种每个线程都需要保存自己副本的情况,比如数据库连接池,Session等
private static ThreadLocal<Connection> connectionHolder  = new ThreadLocal<Connection>() {public Connection initialValue() {    return DriverManager.getConnection(DB_URL);}}; public static Connection getConnection() {return connectionHolder.get();}
                重写了initialValue方法,也就是在线程初始化的时候创建一个数据库连接

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;}
                 用来管理Session

0 0
原创粉丝点击