当前位置: 首页 > news >正文

制作旅游网站成都网站开发公司排名

制作旅游网站,成都网站开发公司排名,机械加工网免费注册,wordpress 官网一、ThreadLocal简介 1、简介 ThreadLocal叫做线程变量,它是一个线程的本地变量,意味着这个变量是线程独有的,是不能与其他线程共享的。这样就可以避免资源竞争带来的多线程的问题。 即 ThreadLocal类用来提供线程内部的局部变量&#xff0…

一、ThreadLocal简介

1、简介

ThreadLocal叫做线程变量,它是一个线程的本地变量,意味着这个变量是线程独有的,是不能与其他线程共享的。这样就可以避免资源竞争带来的多线程的问题。

即 ThreadLocal类用来提供线程内部的局部变量,不同的线程之间不会相互干扰。

ThreadLocal变量,即线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意:

  • 因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这也是 ThreadLocal 命名的由来。
  • 既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。

注意:

  • ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。
  • ThreadLocal 变量通常被 private static修饰。当一个线程(Thread)结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

总的来说,ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景。

2、ThreadLocal与Synchronized区别

ThreadLocal和Synchonized都用于解决多线程并发访问同一个资源对象的时候,可能就会出现线程不安全的问题。

  • ThreadLocal是与一个线程绑定的本地变量,也就意味着这个变量是线程独有的,是不能与其他线程共享的。这样就可以避免资源竞争带来的多线程的问题。
  • 加锁方式(synchronized、Lock) 用于在多个线程间通信时能够获得数据共享。
    但是,ThreadLocal 这种解决多线程安全问题的方式与加锁方式(synchronized、Lock) 是有本质的区别。

两者区别如下:

(1)资源管理方面

  • Synchronized通过加锁的方式,让多个线程之间逐一访问共享资源。
  • ThreadLocal是每个线程都有一个资源副本,是不需要加锁的。

(2)实现方式方面

  • 锁是通过时间换空间的做法。
    Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。
  • ThreadLocal是通过空间换时间的做法。
    ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。

3、使用场景

根据使用场景的不同,我们可以选择不同的技术手段,关键还是要看你的应用场景对于资源的管理是需要多线程之间共享还是单线程内部独享

  • 多线程之间共享资源:使用加锁方式。
  • 单线程内部独享:使用 ThreadLocal变量。

ThreadLocal 适用于如下两种场景:

  • 1)每个线程需要有自己单独的实例。
  • 2)实例需要在多个方法中共享,但不希望被多线程共享。

二、ThreadLocal使用

使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

ThreadLocal类的常用方法:

  • ThreadLocal threadLocal = new ThreadLocal();:创建ThreadLocal对象(即一个线程本地变量)。
  • initialValue():返回此线程局部变量的当前线程的"初始值" 。
  • set(T value):将此线程局部变量的当前线程副本中的值设置为value。。
  • get():返回此线程局部变量的当前线程副本中的值 。
  • remove():移除当前线程绑定的局部变量,该方法可以帮助JVM进行GC。

1、示例1

public class ThreadLocalUseDemo1 {private static ThreadLocal<String> threadLocal1 = new ThreadLocal<>();private static ThreadLocal<Integer> threadLocal2 = new ThreadLocal<>();/*** 运行 count个线程,每个线程持有自己独有的 String类型编号*/public void startThreadArray(int count) {Thread[] runs = new Thread[count];for (int i = 0; i < runs.length; i++) {// 赋值编号idnew ThreadDemo1(i).start();}}/*** 线程类:*/public static class ThreadDemo1 extends Thread {/*** 编号id*/private int codeId;public ThreadDemo1(int codeId) {this.codeId = codeId;}@Overridepublic void run() {String threadName = Thread.currentThread().getName();threadLocal1.set("threadLocal1赋值,线程_" + codeId);if (codeId == 2) {//如果是线程2,设置 threadLocal2变量,值乘以5threadLocal2.set(codeId * 5);}System.out.println(threadName + " -》 " + threadLocal1.get());System.out.println(threadName + " -》 " + threadLocal2.get());// 使用完移除,help GCthreadLocal1.remove();threadLocal2.remove();}}public static void main(String[] args) {ThreadLocalUseDemo1 useDemo = new ThreadLocalUseDemo1();// 启动3个线程useDemo.startThreadArray(3);}}

在这里插入图片描述

从示例1中可以看到,每个线程分别获取了自己线程存放的变量,他们之间变量的获取并不会错乱。

2、示例2

public class ThreadLocalUseDemo2 {//public static ThreadLocalUseDemo2.Number number = new ThreadLocalUseDemo2.Number(0);/*** 初始化 num值。使用时,先通过get方法获取。*/public static ThreadLocal<ThreadLocalUseDemo2.Number> threadLocalValue = new ThreadLocal<ThreadLocalUseDemo2.Number>() {@Overrideprotected Number initialValue() {return new Number(0);}};/*** 数据类*/private static class Number {public Number(int num) {this.num = num;}private int num;public int getNum() {return num;}public void setNum(int num) {this.num = num;}@Overridepublic String toString() {return "Number [num=" + num + "]";}}/*** 线程类:*/public static class ThreadDemo2 extends Thread {@Overridepublic void run() {// 如果没有初始化,注意NPE。// static修饰的 number时,注释掉这句Number number = threadLocalValue.get();//每个线程计数加随机数Random r = new Random();number.setNum(number.getNum() + r.nextInt(100));//将其存储到ThreadLocal中threadLocalValue.set(number);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}//打印保存的随机值System.out.println(Thread.currentThread().getName() + " -》 " + threadLocalValue.get().getNum());threadLocalValue.remove();System.out.println(Thread.currentThread().getName() + " remove方法之后 -》 " + threadLocalValue.get().getNum());}}public static void main(String[] args) {// 启动5个线程for (int i = 0; i < 5; i++) {new ThreadDemo2().start();}}
}

在这里插入图片描述
从示例2中可以看到,每个线程可以通过 initialValue方法初始化变量值 。如果使用 public static ThreadLocalUseDemo2.Number number赋值,会导致数值一样。因为是 static修饰的,所有线程都指向同一个对象。

三、ThreadLocal源码分析

查看 ThreadLocal类的常用方法源码。

1、set()方法

查看 set方法。

    public void set(T value) {// 1、获取当前线程Thread t = Thread.currentThread();// 2、获取线程中的属性 threadLocalMap//如果threadLocalMap 不为空,则直接更新要保存的变量值,否则创建threadLocalMap,并赋值ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);else// 初始化thradLocalMap 并赋值createMap(t, value);}

1.1 ThreadLocalMap简介

ThreadLocalMap是 ThreadLocal的内部静态类,其实 ThreadLocalMap是个标准的 Map实现,内部有一个元素类型为 Entry 的数组,用以存放线程可能需要的多个副本变量。

即 ThreadLocalMap的构成主要是用 Entry来保存数据 ,而且还是继承的弱引用。在 Entry内部使用 ThreadLocal作为key,使用我们设置的value作为value。

查看 ThreadLocalMap类。

    static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}private Entry getEntry(ThreadLocal<?> key) {int i = key.threadLocalHashCode & (table.length - 1);Entry e = table[i];if (e != null && e.get() == key)return e;elsereturn getEntryAfterMiss(key, i, e);}private void set(ThreadLocal<?> key, Object value) {// We don't use a fast path as with get() because it is at// least as common to use set() to create new entries as// it is to replace existing ones, in which case, a fast// path would fail more often than not.Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;// hash冲突时,使用了开放定址法(线性探测再散列即依次向后查找)。e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();if (k == key) {e.value = value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}tab[i] = new Entry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();}private static int nextIndex(int i, int len) {return ((i + 1 < len) ? i + 1 : 0);}// ...}

可以看到 Entry 内部静态类,它继承了 WeakReference(弱引用),一个key是 ThreadLocal<?>类型,一个value是 Object 类型的值。

  • getEntry 方法:是获取某个 ThreadLocal 对应的值。
  • set 方法:是更新或赋值相应的 ThreadLocal对应的值。

1.2 初始化ThreadLocalMap

接着查看 ThreadLocal.createMap方法。

   void createMap(Thread t, T firstValue) {// 创建 ThreadLocalMapt.threadLocals = new ThreadLocalMap(this, firstValue);}//ThreadLocalMap 构造方法ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);}

注意:t.threadLocals 的代表的字段信息。

在这里插入图片描述

通过这里我们发现,每个线程都拥有一个 ThreadLocalMap类用 Entry存放保存数据,在 Entry内部使用 ThreadLocal作为key,使用我们设置的value作为value。从而保证每个线程内部的局部变量副本相互干扰。

2、get方法

查看 get方法。

    public T get() {//1、获取当前线程Thread t = Thread.currentThread();//2、获取当前线程的ThreadLocalMapThreadLocalMap map = getMap(t);if (map != null) {//3.1、获取threalLocalMap中存储的值ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}//3.2 如果是数据为null,则初始化,初始化的结果,TheralLocalMap中存放key值为threadLocal,值为nullreturn setInitialValue();}private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);return value;}

get 方法,其实就是拿到每个线程独有的 ThreadLocalMap。

然后再用 ThreadLocal 的当前实例,拿到 Map 中的相应的 Entry,然后就可以拿到相应的值返回出去。如果 Map 为空,还会先进行 map 的创建,初始化等工作并返回null。

3、remove方法

查看 remove方法。

     public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);}/*** Remove the entry for key.*/private void remove(ThreadLocal<?> key) {Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {if (e.get() == key) {e.clear();expungeStaleEntry(i);return;}}}

remove方法,直接将 ThrealLocal 对应的值从当前相差 Thread中的 ThreadLocalMap中删除。

如果使用完不删除的话,就会涉及到内存泄露的问题。

这是因为 ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用。
弱引用的特点是:如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉。

如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候会被清理掉的,这样一来 ThreadLocalMap中使用这个 ThreadLocal 的 key 也会被清理掉。但是,value 是强引用,不会被清理,这样一来就会出现 key 为 null 的 value。因为,一般定义ThreadLocal 变量通常被 private static修饰。

参考文章:

  • ThreadLocal源码解析:https://blog.csdn.net/qq_26470817/article/details/124993311

– 求知若饥,虚心若愚。

http://www.yayakq.cn/news/239152/

相关文章:

  • 公司手机网站设计有什么专业做心理的网站
  • 电子商务网站开发课程上海知名的seo推广咨询
  • 做网站图结构南京制作网站速成班
  • 电子商务系统 网站建设有个电商网站模板
  • 优秀的网站有哪些教育网站怎么做
  • 滁州市工程建设网站部队内网网站建设方案
  • h5 网站开发流程图新媒体管家
  • 网站建设目标是什么意思临淄区住房和城乡建设局网站
  • 金溪做网站wordpress 食谱主题
  • 成都网站建设028net徐州网站开发培训
  • 网站源码上传到空间以后怎么做神马推广登录
  • 做网站编写上海市建设工程交易平台
  • 梅县区建设工程交易中心网站网站怎么做竞价推广
  • 网络品牌网站建设做网站公司 陕西渭南
  • 徐州市城乡建设局门户网站百度网盘 做网站图床
  • 茶酒行业网站建设wordpress模版 区块链
  • 制作网页和网站的区别汽车网站的建设方向
  • 诺尔诺达网站建设网站开发调研方案
  • 手机网站免费建设河南省建设部网站官网
  • 网站运营服务中心建设方案服务号微网站怎么做
  • 苏州外贸网站大连网站制作机构
  • 速冻蔬菜做哪个国际网站好钟楼做网站
  • 网站前端模板下载网站建设需要哪些人
  • 广州设计企业网站网站建设的参考文献英文
  • 在win10下建设网站沈阳高端做网站建设
  • 服装网站建设企业需求调查wordpress数据库里的主题痕迹
  • 做网站要的软件建设网站要用到什么语言
  • 做一网站要什么软件有哪些科技类网站简介怎么做
  • 类似闲鱼网站怎么做珠海移动网站定制
  • 社交类网站开发ppt制作平台