整站建设和网站优化,制作一个网站需要多少时间,北京网站开发公司排名,京紫元年深圳网站建设ThreadLocal 有什么用#xff1f;
目录
ThreadLocal 有什么用#xff1f;
如何使用 ThreadLocal#xff1f;
ThreadLocal 原理了解吗#xff1f; ThreadLocal 有什么用#xff1f;
通常情况下#xff0c;我们创建的变量是可以被任何一个线程访问并修改的。如果想实现…ThreadLocal 有什么用
目录
ThreadLocal 有什么用
如何使用 ThreadLocal
ThreadLocal 原理了解吗 ThreadLocal 有什么用
通常情况下我们创建的变量是可以被任何一个线程访问并修改的。如果想实现每一个线程都有自己的专属本地变量该如何解决呢
JDK 中自带的ThreadLocal类正是为了解决这样的问题。 ThreadLocal类主要解决的就是让每个线程绑定自己的值可以将ThreadLocal类形象的比喻成存放数据的盒子盒子中可以存储每个线程的私有数据。
如果你创建了一个ThreadLocal变量那么访问这个变量的每个线程都会有这个变量的本地副本这也是ThreadLocal变量名的由来。他们可以使用 get() 和 set() 方法来获取默认值或将其值更改为当前线程所存的副本的值从而避免了线程安全问题。
再举个简单的例子两个人去宝屋收集宝物这两个共用一个袋子的话肯定会产生争执但是给他们两个人每个人分配一个袋子的话就不会出现这样的问题。如果把这两个人比作线程的话那么 ThreadLocal 就是用来避免这两个线程竞争的。
如何使用 ThreadLocal
相信看了上面的解释大家已经搞懂 ThreadLocal 类是个什么东西了。下面简单演示一下如何在项目中实际使用 ThreadLocal 。
import java.text.SimpleDateFormat;
import java.util.Random;public class ThreadLocalExample implements Runnable{// SimpleDateFormat 不是线程安全的所以每个线程都要有自己独立的副本private static final ThreadLocalSimpleDateFormat formatter ThreadLocal.withInitial(() - new SimpleDateFormat(yyyyMMdd HHmm));public static void main(String[] args) throws InterruptedException {ThreadLocalExample obj new ThreadLocalExample();for(int i0 ; i10; i){Thread t new Thread(obj, i);Thread.sleep(new Random().nextInt(1000));t.start();}}Overridepublic void run() {System.out.println(Thread Name Thread.currentThread().getName() default Formatter formatter.get().toPattern());try {Thread.sleep(new Random().nextInt(1000));} catch (InterruptedException e) {e.printStackTrace();}//formatter pattern is changed here by thread, but it wont reflect to other threadsformatter.set(new SimpleDateFormat());System.out.println(Thread Name Thread.currentThread().getName() formatter formatter.get().toPattern());}}输出结果
Thread Name 0 default Formatter yyyyMMdd HHmm
Thread Name 0 formatter yy-M-d ah:mm
Thread Name 1 default Formatter yyyyMMdd HHmm
Thread Name 2 default Formatter yyyyMMdd HHmm
Thread Name 1 formatter yy-M-d ah:mm
Thread Name 3 default Formatter yyyyMMdd HHmm
Thread Name 2 formatter yy-M-d ah:mm
Thread Name 4 default Formatter yyyyMMdd HHmm
Thread Name 3 formatter yy-M-d ah:mm
Thread Name 4 formatter yy-M-d ah:mm
Thread Name 5 default Formatter yyyyMMdd HHmm
Thread Name 5 formatter yy-M-d ah:mm
Thread Name 6 default Formatter yyyyMMdd HHmm
Thread Name 6 formatter yy-M-d ah:mm
Thread Name 7 default Formatter yyyyMMdd HHmm
Thread Name 7 formatter yy-M-d ah:mm
Thread Name 8 default Formatter yyyyMMdd HHmm
Thread Name 9 default Formatter yyyyMMdd HHmm
Thread Name 8 formatter yy-M-d ah:mm
Thread Name 9 formatter yy-M-d ah:mm从输出中可以看出虽然 Thread-0 已经改变了 formatter 的值但 Thread-1 默认格式化值与初始化值相同其他线程也一样。
上面有一段代码用到了创建 ThreadLocal 变量的那段代码用到了 Java8 的知识它等于下面这段代码如果你写了下面这段代码的话IDEA 会提示你转换为 Java8 的格式(IDEA 真的不错)。因为 ThreadLocal 类在 Java 8 中扩展使用一个新的方法withInitial()将 Supplier 功能接口作为参数。
private static final ThreadLocalSimpleDateFormat formatter new ThreadLocalSimpleDateFormat(){Overrideprotected SimpleDateFormat initialValue(){return new SimpleDateFormat(yyyyMMdd HHmm);}
};ThreadLocal 原理了解吗
从 Thread类源代码入手。
public class Thread implements Runnable {//......//与此线程有关的ThreadLocal值。由ThreadLocal类维护ThreadLocal.ThreadLocalMap threadLocals null;//与此线程有关的InheritableThreadLocal值。由InheritableThreadLocal类维护ThreadLocal.ThreadLocalMap inheritableThreadLocals null;//......
}从上面Thread类 源代码可以看出Thread 类中有一个 threadLocals 和 一个 inheritableThreadLocals 变量它们都是 ThreadLocalMap 类型的变量,我们可以把 ThreadLocalMap 理解为ThreadLocal 类实现的定制化的 HashMap。默认情况下这两个变量都是 null只有当前线程调用 ThreadLocal 类的 set或get方法时才创建它们实际上调用这两个方法的时候我们调用的是ThreadLocalMap类对应的 get()、set()方法。
ThreadLocal类的set()方法。
public void set(T value) {//获取当前请求的线程Thread t Thread.currentThread();//取出 Thread 类内部的 threadLocals 变量(哈希表结构)ThreadLocalMap map getMap(t);if (map ! null)// 将需要存储的值放入到这个哈希表中map.set(this, value);elsecreateMap(t, value);
}
ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}通过上面这些内容我们足以通过猜测得出结论最终的变量是放在了当前线程的 ThreadLocalMap 中并不是存在 ThreadLocal 上ThreadLocal 可以理解为只是ThreadLocalMap的封装传递了变量值。 ThrealLocal 类中可以通过Thread.currentThread()获取到当前线程对象后直接通过getMap(Thread t)可以访问到该线程的ThreadLocalMap对象。
每个Thread中都具备一个ThreadLocalMap而ThreadLocalMap可以存储以ThreadLocal为 key Object 对象为 value 的键值对。
比如我们在同一个线程中声明了两个ThreadLocal对象的话 Thread内部都是使用仅有的那个ThreadLocalMap存放数据的ThreadLocalMap的 key 就是 ThreadLocal对象value 就是 ThreadLocal对象调用set方法设置的值。ThreadLocal 内存泄露问题是怎么导致的
ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用而 value 是强引用。所以如果 ThreadLocal 没有被外部强引用的情况下在垃圾回收的时候key 会被清理掉而 value 不会被清理掉。
这样一来ThreadLocalMap 中就会出现 key 为 null 的 Entry。假如我们不做任何措施的话value 永远无法被 GC 回收这个时候就可能会产生内存泄露。ThreadLocalMap 实现中已经考虑了这种情况在调用 set()、get()、remove() 方法的时候会清理掉 key 为 null 的记录。使用完 ThreadLocal方法后 最好手动调用remove()方法。
static class Entry extends WeakReferenceThreadLocal? {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal? k, Object v) {super(k);value v;}
}弱引用 如果一个对象只具有弱引用那就类似于可有可无的生活用品。弱引用与软引用的区别在于只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中一旦发现了只具有弱引用的对象不管当前内存空间足够与否都会回收它的内存。不过由于垃圾回收器是一个优先级很低的线程 因此不一定会很快发现那些只具有弱引用的对象。 弱引用可以和一个引用队列ReferenceQueue联合使用如果弱引用所引用的对象被垃圾回收Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。