ThreadLocal类
2014-04-19 18:45
316 查看
synchronized这类线程同步的机制可以解决多线程并发问题,在这种解决方案下,多个线程访问到的,都是同一份变量的内容。为了防止在多线程访问的过程中,可能会出现的并发错误。不得不对多个线程的访问进行同步,这样也就意味着,多个线程必须先后对变量的值进行访问或者修改,这是一种以延长访问时间来换取线程安全性的策略。
而ThreadLocal类为每一个线程都维护了自己独有的变量拷贝。每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了,那就没有任何必要对这些线程进行同步,它们也能最大限度的由CPU调度,并发执行。并且由于每个线程在访问该变量时,读取和修改的,都是自己独有的那一份变量拷贝,变量被彻底封闭在每个访问的线程中,并发错误出现的可能也完全消除了。对比前一种方案,这是一种以空间来换取线程安全性的策略。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为一个线程对象,而值对应线程的变量副本。
一个简单的模拟ThreadLocal类的实现如下:
结果如下:
thread[a] sn[1]
thread[a] sn[2]
thread[a] sn[3]
thread[b] sn[1]
thread[b] sn[2]
thread[b] sn[3]
thread[c] sn[1]
thread[c] sn[2]
thread[c] sn[3]
使用SimpleThreadLocal也能达到同样的结果:
在定义ThreadLocal变量时,可以采用泛型的模式,定义了ThreadLocal中的Map和各方法的类型,省去了类型转换
重写后我们就不用再要得到一个对象时,先调用set()方法设置我们需要的对象,直接在initialValue中写好就行了。
下面是set()方法的源码:
以下是一个经典的ThreadLocal的应用:
而ThreadLocal类为每一个线程都维护了自己独有的变量拷贝。每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了,那就没有任何必要对这些线程进行同步,它们也能最大限度的由CPU调度,并发执行。并且由于每个线程在访问该变量时,读取和修改的,都是自己独有的那一份变量拷贝,变量被彻底封闭在每个访问的线程中,并发错误出现的可能也完全消除了。对比前一种方案,这是一种以空间来换取线程安全性的策略。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为一个线程对象,而值对应线程的变量副本。
一个简单的模拟ThreadLocal类的实现如下:
import java.util.Collections; import java.util.HashMap; import java.util.Map; public class SimpleThreadLocal { private Map valueMap = Collections.synchronizedMap(new HashMap()); public void set(Object newValue) { valueMap.put(Thread.currentThread(), newValue);//①键为线程对象,值为本线程的变量副本 } public Object get() { Thread currentThread = Thread.currentThread(); Object o = valueMap.get(currentThread);// ②返回本线程对应的变量 if (o == null && !valueMap.containsKey(currentThread)) {// ③如果在Map中不存在,放到Map中保存起来。 o = initialValue(); valueMap.put(currentThread, o); } return o; } public void remove() { valueMap.remove(Thread.currentThread()); } public Object initialValue() { return null; } }一个ThreadLocal的示例如下:
public class ThreadLocalTest { private static ThreadLocal seqNum = new ThreadLocal(){ protected Integer initialValue() { return 0; } }; public int getNextNum(){ seqNum.set((Integer)seqNum.get()+1); return (Integer)seqNum.get(); } public static void main(String[] args) { ThreadLocalTest sn = new ThreadLocalTest(); new Thread(new TestClient(sn),"a").start(); new Thread(new TestClient(sn),"b").start(); new Thread(new TestClient(sn),"c").start(); } } class TestClient implements Runnable{ private ThreadLocalTest sn; public TestClient(ThreadLocalTest sn){ this.sn = sn; } @Override public void run() { for(int i = 0; i < 3; i++){ System.out.println("thread["+Thread.currentThread().getName()+"] sn["+sn.getNextNum()+"]"); } } }
结果如下:
thread[a] sn[1]
thread[a] sn[2]
thread[a] sn[3]
thread[b] sn[1]
thread[b] sn[2]
thread[b] sn[3]
thread[c] sn[1]
thread[c] sn[2]
thread[c] sn[3]
使用SimpleThreadLocal也能达到同样的结果:
public class ThreadLocalTest2 { private static SimpleThreadLocal seqNum = new SimpleThreadLocal(){ public Integer initialValue() { return 0; } }; public int getNextNum(){ seqNum.set((Integer)seqNum.get()+1); return (Integer)seqNum.get(); } public static void main(String[] args) { ThreadLocalTest2 sn = new ThreadLocalTest2(); new Thread(new TestClient2(sn),"a").start(); new Thread(new TestClient2(sn),"b").start(); new Thread(new TestClient2(sn),"c").start(); } } class TestClient2 implements Runnable{ private ThreadLocalTest2 sn; public TestClient2(ThreadLocalTest2 sn){ this.sn = sn; } @Override public void run() { for(int i = 0; i < 3; i++){ System.out.println("thread["+Thread.currentThread().getName()+"] sn["+sn.getNextNum()+"]"); } } }
在定义ThreadLocal变量时,可以采用泛型的模式,定义了ThreadLocal中的Map和各方法的类型,省去了类型转换
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class ConnectionManager { private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { @Override protected Connection initialValue() { Connection conn = null; try { conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/test", "username", "password"); } catch (SQLException e) { e.printStackTrace(); } return conn; } }; public static Connection getConnection() { return connectionHolder.get(); } public static void setConnection(Connection conn) { connectionHolder.set(conn); } }一般来说,会重写initialValue()方法,这样可以在第一次调用get方法时,给出我们想要的对象,默认的initialValue()方法中返回的是null,下面是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(); }
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; }
protected T initialValue() { return null; }
重写后我们就不用再要得到一个对象时,先调用set()方法设置我们需要的对象,直接在initialValue中写好就行了。
下面是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); }
以下是一个经典的ThreadLocal的应用:
import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.cfg.Configuration; /** * Configures and provides access to Hibernate sessions, tied to the * current thread of execution. Follows the Thread Local Session * pattern, see {@link http://hibernate.org/42.html }. */ public class HibernateSessionFactory { /** * Location of hibernate.cfg.xml file. * Location should be on the classpath as Hibernate uses * #resourceAsStream style lookup for its configuration file. * The default classpath location of the hibernate config file is * in the default package. Use #setConfigFile() to update * the location of the configuration file for the current session. */ private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml"; private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>(); private static Configuration configuration = new Configuration(); private static org.hibernate.SessionFactory sessionFactory; private static String configFile = CONFIG_FILE_LOCATION; static { try { configuration.configure(configFile); sessionFactory = configuration.buildSessionFactory(); } catch (Exception e) { System.err .println("%%%% Error Creating SessionFactory %%%%"); e.printStackTrace(); } } private HibernateSessionFactory() { } /** * Returns the ThreadLocal Session instance. Lazy initialize * the <code>SessionFactory</code> if needed. * * @return Session * @throws HibernateException */ public static Session getSession() throws HibernateException { Session session = (Session) threadLocal.get(); if (session == null || !session.isOpen()) { if (sessionFactory == null) { rebuildSessionFactory(); } session = (sessionFactory != null) ? sessionFactory.openSession() : null; threadLocal.set(session); } return session; } /** * Rebuild hibernate session factory * */ public static void rebuildSessionFactory() { try { configuration.configure(configFile); sessionFactory = configuration.buildSessionFactory(); } catch (Exception e) { System.err .println("%%%% Error Creating SessionFactory %%%%"); e.printStackTrace(); } } /** * Close the single hibernate session instance. * * @throws HibernateException */ public static void closeSession() throws HibernateException { Session session = (Session) threadLocal.get(); threadLocal.set(null); if (session != null) { session.close(); } } /** * return session factory * */ public static org.hibernate.SessionFactory getSessionFactory() { return sessionFactory; } /** * return session factory * * session factory will be rebuilded in the next call */ public static void setConfigFile(String configFile) { HibernateSessionFactory.configFile = configFile; sessionFactory = null; } /** * return hibernate configuration * */ public static Configuration getConfiguration() { return configuration; } }
相关文章推荐
- IIS 发布PHP
- C#学习笔记1
- 为什么说淘宝创业已难赚钱?
- MVC#概述
- 并查集 LA 3644
- 基于JFinal的开源博客系统JFinal_Blog1.1版本发布
- 用JavaScript写的一个自定义弹出式对话框
- 堆排序算法的实现
- curl获取网页内容
- lvs-DR之同一网段的实现方法
- android平台接入服务器总结(三)迅雷 多酷 小米
- hadoop 伪分布安装
- 黑马程序员_ 黑马程序员_基础加强第二天——代理
- 朴素贝叶斯分类器
- 指针与数
- mac上Sublime Text2 搭建lua
- php编译时undefined reference to `libiconv_open'解决办法
- shell脚本中如何获取命令的参数(1)
- linux 基本命令
- Lync 小技巧-32-无TMG-internet-Lync 2013 For iPhone如何做