一、前言

  ThreadLocal这个对象就是为多线程而生的,没有了多线程ThreadLocal就没有存在的必要了。可以将任何你想在每个线程独享的对象放置其中,并在任何时候取出来。

二、基本用法

  ThreadLocal的使用方法其实特别简单:

  1. 在某个类(相当于工厂类,因为当获取不到的时候你要创建一个给它)中创建静态的ThreadLocal。
  2. 在别的地方调用它的set和get方法用来存放对象。

  下面展示一个样例:

package yiwangzhibujian;

public class ThreadLocalUse{
//创建一个静态的ThreadLocal对象,当做仓库,存储每个线程自己的资源
//此处用Object来代替,可以使连接Connection
private static ThreadLocal<Object> store=new ThreadLocal<Object>(); //当每个线程要获取一个线程资源的时候,调用get方法
public Object get(){
//首先去仓库中去寻找
Object obj=store.get();
//如果不存在那就创建一个给线程,并把创建的放到仓库中
if(obj==null){
obj=new Object();
set(obj);
}
return obj;
} //想将线程资源放到仓库调用set方法,
public void set(Object obj){
store.set(obj);
}
}

  这是一个使用模板,可以根据具体情况来做相应改变。

三、ThreadLocal理解

3.1 ThreadLocal会导致内存泄漏吗

  首先明确表示使用ThreadLocal不会导致内存泄漏。现在做如下测试证明这个观点,有如下程序:

public class ThreadLocalTest{
//创建一个ThreadLocal用来存储每个线程的对象
private static ThreadLocal<Object> local=new ThreadLocal<Object>();
//一个常量,用来创建10M大小的对象使用
private static final int _1MB=1024*1024; public static void main(String[] args) throws Exception{
//创建一个Runnable对象
Runnable r=new Runnable(){
@Override
public void run(){
System.out.println("子线程内创建对象并放置ThreadLocal中");
//创建一个10M大小的数组,并将其放到ThreadLocal中
byte[] data=new byte[10*_1MB];
local.set(data);
System.out.println("子线程运行结束,代表线程结束");
}
};
//创建线程并运行
Thread t=new Thread(r);
t.start(); System.out.println("主线程休眠一秒,为了让创建的子线程执行完毕");
Thread.sleep(1000);
System.out.println("休眠完成,主线程准备分配一个10M大小的数组");
byte[] data2=new byte[10*_1MB];
System.out.println("分配数组完毕");
}
}

  在程序右键点击Run as->Run Configurations进行虚拟机配置:

  配置如下,-XX:+PrintGCDetails(打印垃圾回收日志) -XX:+PrintGCTimeStamps(打印回收时间,可不用) -Xms18M -Xmx18M(设置初始化和最大内存为18M,这么做的目的是,当创建两个10M大小的数组时,会内存溢出还是将ThreadLocal内的进行回收):

  首先介绍这个程序的目的就是创建一个对象放置到ThreadLocal中,看看垃圾回收器会不会在线程结束的时候对其进行回收。运行结果如下:

主线程休眠一秒,为了让创建的子线程执行完毕
子线程内创建对象并放置ThreadLocal中
子线程运行结束,代表线程结束
休眠完成,主线程准备分配一个10M大小的数组
1.102: [GC [PSYoungGen: 376K->224K(5376K)] 10616K->10464K(17664K), 0.0077785 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
1.110: [GC [PSYoungGen: 224K->224K(5376K)] 10464K->10464K(17664K), 0.0008971 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
1.111: [Full GC [PSYoungGen: 224K->0K(5376K)] [PSOldGen: 10240K->164K(12288K)] 10464K->164K(17664K) [PSPermGen: 3030K->3030K(21248K)], 0.0058052 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
分配数组完毕

  可以发现,当主线程准备分配一个10M大小的数组时发现内存不够,会调用垃圾处理器进行回收,通过[PSOldGen: 10240K->164K(12288K)] 可以看出,放入ThreadLocal中的对象被回收了。

3.2 使用全局Map替换ThreadLocal可不可以

  我曾经也想过使用全局Map来替换ThreadLocal,即使用如下代码表示展示的样例:

public class ThreadLocalUse{

    private static Map<Thread,Object> store=new HashMap<Thread,Object>();

    //当每个线程要获取一个线程资源的时候,调用get方法
public Object get(){
//首先去仓库中去寻找
Object obj=store.get(Thread.currentThread());
//如果不存在那就创建一个给线程,并把创建的放到仓库中
if(obj==null){
obj=new Object();
set(obj);
}
return obj;
} //想将线程资源放到仓库调用set方法,
public void set(Object obj){
store.put(Thread.currentThread(),obj);
}
}

  这样也可以做到线程内共享对象,但是有一个致命的缺点,那就是当线程结束后,存储在Map中的对象必须手动清除,否则将会导致内存泄漏。

3.3 对比ThreadLocal和synchronized同步机制

  很多人都会对这两个对象进行比对,我也谈一下我自己的想法。

  使用synchronized是为了将多条语句进行原子化操作,比说对于递增操作i++,任意一个线程在执行代码时都要保证别的线程不能执行这个代码,否则就会产生脏数据,使用synchronized可以避免这一点。

  而使用ThreadLocal就是给每个线程存储对象用的。既然每个线程使用了自己的对象,没有了竞争就不会出现多线程相关的问题。

3.4 我对ThreadLocal的一点点理解

  本来呢,我也一直在想ThreadLocal设计的有一点点不合理,比如为什么只能存储一个对象呢,存储多个对象多好呀,或者Thread直接内置一个Map多好,就可以这样操作:

Thread.currentThread().map.put(key,value);
Thread.currentThread().map.get(key);

  我觉的我的想法很不错,但是至于他们为什么这个设计或许有别的意思。

  这一篇讲解了ThreadLocal,虽然和它和并发没有多大关系,但是它毕竟因多线程而生,所以还是涉及到它。

  未经许可禁止转载。

【Java并发系列03】ThreadLocal详解的更多相关文章

  1. 【转】Java并发的AQS原理详解

    申明:此篇文章转载自:https://juejin.im/post/5c11d6376fb9a049e82b6253写的真的很棒,感谢老钱的分享. 打通 Java 任督二脉 —— 并发数据结构的基石 ...

  2. Java 并发编程 | 线程池详解

    原文: https://chenmingyu.top/concurrent-threadpool/ 线程池 线程池用来处理异步任务或者并发执行的任务 优点: 重复利用已创建的线程,减少创建和销毁线程造 ...

  3. Java 并发:Lock 框架详解

    摘要: 我们已经知道,synchronized 是java的关键字,是Java的内置特性,在JVM层面实现了对临界资源的同步互斥访问,但 synchronized 粒度有些大,在处理实际问题时存在诸多 ...

  4. java 并发编程lock使用详解

    浅谈Synchronized: synchronized是Java的一个关键字,也就是Java语言内置的特性,如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,执行代码块时,其 ...

  5. Java网络编程和NIO详解开篇:Java网络编程基础

    Java网络编程和NIO详解开篇:Java网络编程基础 计算机网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为 ...

  6. Java网络编程和NIO详解8:浅析mmap和Direct Buffer

    Java网络编程与NIO详解8:浅析mmap和Direct Buffer 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NI ...

  7. Java网络编程和NIO详解9:基于NIO的网络编程框架Netty

    Java网络编程和NIO详解9:基于NIO的网络编程框架Netty 转自https://sylvanassun.github.io/2017/11/30/2017-11-30-netty_introd ...

  8. Java网络编程和NIO详解6:Linux epoll实现原理详解

    Java网络编程和NIO详解6:Linux epoll实现原理详解 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NIO h ...

  9. java的PrintStream(打印输出流)详解(java_io)

    java的PrintStream(打印输出流)详解(java_io) 本章介绍PrintStream以及 它与DataOutputStream的区别.我们先对PrintStream有个大致认识,然后再 ...

随机推荐

  1. laravel安装插件laravel-ide-helper

    1.插件位置laravel-ide-helper https://github.com/barryvdh/laravel-ide-helper 2.首先改变镜像源为国内的镜像源 P { margin- ...

  2. sql server 数值的四舍五入

    sql中的四舍五入通常会有round  和cast( …… as decimal())两种方式: 个人建议使用cast  方式: 方式1: 经过试验,同样都可以做到四舍五入,但round如下实例1会报 ...

  3. 学习css之文本属性

    css3之文本属性: 1.缩进和水平对齐:text-indent, 通过使用 text-indent 属性,所有元素的第一行都可以缩进一个给定的长度,甚至该长度可以是负值. 这个属性最常见的用途是将段 ...

  4. java复习(2)---java基础杂记

    java命名规范: 参考:http://www.cnblogs.com/maowang1991/archive/2013/06/29/3162366.html 1.项目名小写 2.包名小写 3.类名每 ...

  5. .NET 二维码生成(ThoughtWorks.QRCode)【转发jiangys】

    .NET 二维码生成(ThoughtWorks.QRCode) 2015-06-21 22:19 by jiangys, 3790 阅读, 8 评论, 收藏, 编辑 引用ThoughtWorks.QR ...

  6. MySQL 的Coalesce函数

    今天用到了coalesce 函数,原因在于,我想要查找合同到期日的字段是否有值(因为合同到期日分3个字段,对应着不同的日期) select coalesce(contract_date1,contra ...

  7. stl_algorithm算法之排序算法

    排序算法: 注意:容器中必须重载 op< ,排序中stl标准中要求用小于来进行比较. 7.53.sort //全排序. 7.54.stable_sort //稳定排序.两个或两个以上的相邻且相等 ...

  8. Java面试小试题

    面试是我们每个人都要经历的事情,大部分人且不止一次,这里给大家总结最新的2016年面试题,让大家在2017年找工作时候能够事半功倍. 1 Switch能否用string做参数? a.在 Java 7 ...

  9. IOS中的绘图Quartz2D

    drawRect 方法的使用 常见图形的绘制:线条.多边形.圆 绘图状态的设置:文字颜色.线宽等 图形上下文状态的保存与恢复 图形上下文栈 Quartz 2D是一个二维绘图引擎,同时支持IOS和MAC ...

  10. bootstrap 获得轮播中的索引或当前活动的焦点对象

    今天用bootstrap做一个轮播,当轮播滚到每张图的时候,在页面下面就显示相对应的内容,那么问题来了:为了轮播图的可扩展性,我们肯定需要知道当前活动(显示图片)的索引号,查了bootstrap文档, ...