`
serenity
  • 浏览: 61048 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

ThreadLocal学习笔记

阅读更多

         Javajava.lang.ThreadLocal类不是一个线程的本地实现,而是线程局部变量,也许叫ThreadlocalVar更加合适。ThreaLocal使用非常简单,就是为使用该变量的线程提供一个变量值的副本,每个线程都可以独立的改变自己的副本,而不会与其他线程冲突。


     从线程角度看,每个线程都保持一个对其线程局部变量的隐式引用,只要线程是活动并且ThreadLocal实例可访问。线程结束之后,相应的线程局部变量的所有副本都会被垃圾回收。

 

        通过ThreadLocal存取的数据,总是与当前线程有关。Jvm为每个运行的线程,绑定了私有的本地实例存取空间,每一个线程都可以独立地改变空间内容而不会和其他线程副本冲突,从而隔离了线程,解决的并发访问问题。

      

      ThreadLocal的实现思路很简单,在其内部有一个Map,用户存储每一个线程变量副本。


      对已多线程资源共享问题,有两种解决方式,一种同步机制,以时间换空间,不同的线程排队访问。一种是ThreadLocal,以空间换时间,为每个线程都复制一份共享资源的副本,线程直接访问自己的副本,因此可以同时访问互不影响。


     ThreadLocal主要由四个方法组成initialValue()get()set(T)remove(),其中值得注意的是initialVlaue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初十值。这是方法是一个延时调用的方法,在一个线程第一次调用get()或者set(object )时才执行,丙炔仅执行1次。ThreadLocal中确实实现直接返回一个null

  

     使用ThreadLocal的典型实例就是Hibernate中的session工具类HibernateUtil,该类用于session管理。

 

public class HibernateUtil {
    private static Log log = LogFactory.getLog(HibernateUtil.class);
    private static final SessionFactory sessionFactory;     //定义SessionFactory
 
    static {
        try {
            // 通过默认配置文件hibernate.cfg.xml创建SessionFactory
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            log.error("初始化SessionFactory失败!", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    //创建线程局部变量session,用来保存Hibernate的Session
    public static final ThreadLocal session = new ThreadLocal();
 
    /**
     * 获取当前线程中的Session
     * @return Session
     * @throws HibernateException
     */
    public static Session currentSession() throws HibernateException {
        Session s = (Session) session.get();
        // 如果Session还没有打开,则新开一个Session
        if (s == null) {
            s = sessionFactory.openSession();
            session.set(s);         //将新开的Session保存到线程局部变量中
        }
        return s;
    }
 
    public static void closeSession() throws HibernateException {
        //获取线程局部变量,并强制转换为Session类型
        Session s = (Session) session.get();
        session.set(null);
        if (s != null)
            s.close();
    }
}

 

     在这个类中,由于没有重写ThreadLocalinitialValue()方法,则首次创建线程局部变量session的初始值为null,第一次调用currentSesion()的时候,线程局部变量的get()方法也为null。因此,对session做判断,如果为null,则打开一个新的会话,并保存线程局部变量session中,这一步非常关键,这也是“public static final ThreadLocal seesion = new ThreadLocal()”所创建对象session能强制转换为Hibernate Session对象的原因。


     ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。


     下面是自己写的一个小例子,创建一个Bean,通过不同的线程对象设置Bean属性,保证各个线程Bean对象的独立性。

 

public class Bean {
	
	private int num;

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}
}

 

public class ThreadLocalDemo implements Runnable {
	//创建线程局部变量,用来保存bean
	private static final ThreadLocal<Bean> beanLocal = new ThreadLocal<Bean>();
	
	public Bean getBean(){
		//获取线程本地变量
		Bean bean = beanLocal.get();
		//如何还没有设置,则设置。(第一次肯定为空)
		if(bean==null){
			//将新建的bean保存到线程本地变量
			bean = new Bean();
			beanLocal.set(bean);
		}
		return bean;
	}
	
	public void test(){
		//获取当前线程名字
		String threadName = Thread.currentThread().getName();
		System.out.println("currThreadName:"+threadName);
		//生成随机数
		Random random = new Random();
		int num = random.nextInt(10);
		//获取当前线程本地变量bean,并将num设置到bean变量中
		Bean bean = getBean();
		bean.setNum(num);
		System.out.println("threadName"+threadName+",BeanInt:"+num);
		
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("currThreadName:"+threadName+", currrThreadBeanName:"+bean.getNum());
	}

	@Override
	public void run() {
		test();
	}
	
	public static void main(String[] args){
		ThreadLocalDemo demo = new ThreadLocalDemo();
		Thread td1 = new Thread(demo,"demo1");
		Thread td2 = new Thread(demo,"demo2");
		td1.start();
		td2.start();
	}

}
 

 

 

分享到:
评论

相关推荐

    Java并发编程学习笔记

    3、ThreadLocal 的底层实现与使用 4、ReentrantLock底层实现和如何使用 5、Condition源码分析 6、ReentrantReadWriteLock底层实现原理 7、并发工具类CountDownLatch 、CyclicBarrier和Semaphore底层实现原理 8、...

    java线程学习笔记

    2.3 线程本地存储(Java.lang.ThreadLocal) 15 2.4 线程阻塞 17 2.4.1 调用sleep(millisecond)使任务进入休眠状态 17 2.4.2 等待输出与输入 17 2.4.3 对象锁不可用 17 2.4.4 通过wait()使线程挂起。 17 2.5 线程...

    Java学习笔记-个人整理的

    \contentsline {chapter}{Contents}{2}{section*.1} {1}Java基础}{17}{chapter.1} {1.1}基本语法}{17}{section.1.1} {1.2}数字表达方式}{17}{section.1.2} {1.3}补码}{19}{section.1.3} {1.3.1}总结}{23}{...

    Java并发编程(学习笔记).xmind

    ThreadLocal类 只读共享:不变对象一定是线程安全的 尽量将域声明为final类型,除非它们必须是可变的 分类 不可变对象 事实不可变对象 线程安全共享 封装有助于管理复杂度 线程...

    java并发学习笔记

    文章目录1 线程基础、线程之间的共享与协作1.1 cpu时间片轮询机制1.2 ...ThreadLocal1.11 wait()、notify()和notifyAll()方法1.11.1 锁池和等待池1.11.2 wait()方法1.11.3 notify()和notifyAll()方法1.11.4 wait()和...

    java8源码-ac_babel:一些后端学习笔记整理

    java8 源码 设计模式 java 并发 public class Foo { // SimpleDateFormat is not thread-safe, so ...ThreadLocal ...ThreadLocal(){ ...ThreadLocal&lt;Map&gt;&gt; ...ThreadLocal.withInitial(HashMap::new); public

    javajdk源码学习-JavaSourceLearn:JDK源码学习

    逐步阅读源码添加注释、notes文件夹添加笔记 计划学习任务计划 标题为包名,后面序号为优先级1-4,优先级递减 java.lang Object 1 String 1 AbstractStringBuilder 1 StringBuffer 1 StringBuilder 1 Boolean 2 Byte...

    Android Handler机制的工作原理详析

    上一次写完Binder学习笔记之后,再去看一遍Activity的启动流程,因为了解了Binder的基本原理,这次看印象会更深一点,学习效果也比以前好很多。本来打算直接来写Activity的启动流程的,但总觉得Handler也需要写一下...

    百度地图开发java源码-MD-Notes:计组、操作系统、数据结构、网络IO、Redis、MySQL、JVM等笔记

    业余时间学习技术的同时,做一些记录和总结并乐于分享。 日常主要接触 Web 前后端开发、Linux 运维等,有 Java / Python 后端经验,有大数据开发 Flink,Spark 实战经验,熟练运用 MySQL,Redis,Zookeeper 及常用...

Global site tag (gtag.js) - Google Analytics