Java并发基础--ThreadLocal

1、ThreadLocal定义

ThreadLocal是一个能够提供线程局部变量的类,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路,经过为每一个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在不少状况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。redis

ThreadLocal的做用是提供线程内的局部变量,这种变量在线程的生命周期内起做用。做用:提供一个线程内公共变量(好比本次请求的用户信息),减小同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度,或者为线程提供一个私有的变量副本,这样每个线程均可以随意修改本身的变量副本,而不会对其余线程产生影响。数据库

2、基本使用

 1 public class ThreadLocalDemo {  2     public static ThreadLocal<Person> local = new ThreadLocal<Person>(){  3         protected Person initialValue() {  4             return new Person(Thread.currentThread().getName());  5  };  6  };  7     public static Person getPerson(){  8         return local.get();  9  } 10     
11     public static void main(String[] args) { 12         for(int i=0;i<5;i++){ 13             new Thread(new Runnable() { 14  @Override 15                 public void run() { 16                     System.out.println(Thread.currentThread().getName()+":"+ThreadLocalDemo.getPerson().getName()); 17  } 18  }).start(); 19  } 20         System.out.println(Thread.currentThread().getName()+":"+ThreadLocalDemo.getPerson().getName()); 21  } 22 
23 } 24 
25 class Person{ 26     private String name; 27     
28     public Person(String name){ 29         this.name = name; 30  } 31     public String getName() { 32         return name; 33  } 34 
35     public void setName(String name) { 36         this.name = name; 37  } 38     
39 }

结果输出:安全

main:main Thread-0:Thread-0 Thread-2:Thread-2 Thread-4:Thread-4 Thread-1:Thread-1 Thread-3:Thread-3

从结果能够看出,local对象针对不一样的线程提供的Person变量是不一样的。而且互不影响。同一个线程可以共享local中保存的对象。session

3、源码分析

1.get()方法,用来获取当前调用get方法的线程对应在ThreadLocal中保存的变量的副本。多线程

public T get() { Thread t = Thread.currentThread();//获取当前线程
        ThreadLocalMap map = getMap(t);//经过线程获取对应的ThreadLocalMap,ThreadLocalMap是ThreadLocal中的一个静态内部类,可将他看作一个特殊的map,key是ThreadLocal,value则是保存的变量的值。
        if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }

经过线程t获取,在Thread类中定义了ThreadLocal.ThreadLocalMap threadLocals = null;即将线程和ThreadLocalMap联系起来,初始时,threadLocals=null,则须要经过setInitialValue方法建立,具体以下:并发

private T setInitialValue() { T value = initialValue();//调用ThreadLocal中的initialValue方法获取变量的初始值
        Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value);//当线程关联的ThreadLocalMap为null的时候,建立一个ThreadLocalMap,并赋值让线程t的threadLocals引用指向新建立的ThreadLocalMap对象。
        return value; } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }

经过ThreadLocal的源码分析,能够知道每一个线程都有一个ThreadLocalMap类型的引用threadLocals,线程初始时候为null,当咱们调用set或者get方法的时候,经过当前线程获取ThreadLocalMap,若是不为null,则经过当前threadLocal做为key,获取对应的value值。若是为null,则new ThreadLocalMap(this, firstValue);,this指向ThreadLocal对象,firstValue则是initialValue()方法中指定的初始值。ide

2.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); } //经过线程类Thread获取关联的ThreadLocalMap对象,若是不为null,直接调用ThreadLocalMap的set方法,设置键值对,key为ThreadLocal对象,value则为设置的值。

3.remove()方法源码分析

public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }

4、应用场景

数据库链接,session管理,redis链接等。例如经典的使用,以下所示:this

 1 public class JdbcTemplateImpl {  2     // 数据库用户名
 3     private String username;  4     // 数据库密码
 5     private String password;  6     // 驱动信息
 7     private String driver;  8     // 数据库地址
 9     private String url; 10     // 声明线程共享变量
11     public static ThreadLocal<Connection>    connections= new ThreadLocal<Connection>(); 12     
13     public JdbcTemplateImpl(String driver, String url, String username, String password) { 14         this.driver = driver; 15         this.url = url; 16         this.username = username; 17         this.password = password; 18         try { 19             Class.forName(this.driver); 20         } catch (Exception e) { 21  e.printStackTrace(); 22  } 23  } 24     
25     public Connection getConnection() { 26         Connection connection = connections.get(); 27         try { 28             if (connection != null && !connection.isClosed()) { 29                 return connection; 30             }else { 31                 connection = DriverManager.getConnection(this.url, this.username, this.password); 32  connections.set(connection); 33  } 34         } catch (SQLException e) { 35  e.printStackTrace(); 36  } 37         return connection; 38  } 39 }
相关文章
相关标签/搜索