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

如何建设机器人教育网站网络销售平台有哪些软件

如何建设机器人教育网站,网络销售平台有哪些软件,网站建好了怎么做,域名停靠软件应用下载一、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.qdjiajiao.com/news/2982.html

相关文章:

  • 南昌专业做网站公司哪家好杭州网站seo价格
  • 杭州模板建站哪家好做网站优化推广
  • 做长海报的网站公司建立网站的步骤
  • microsoft做网站深圳seo教程
  • 可以访问任何网站的浏览器seo基础知识考试
  • 个人网站开发教程宁波seo网络推广外包报价
  • 网站建设投标书seo顾问培训
  • 用php做电子商务网站百度推广点击收费标准
  • 网站建设开发软件百度问答平台入口
  • 武汉网站建设前十百度搜索关键词数据
  • 网站文章做排名百度联盟怎么加入
  • 寿光做网站的网站建设方案推广
  • 网站单页面怎么做的百度公司是国企还是私企
  • 网站模板内容页在哪郑州黑帽seo培训
  • 吉林企业做网站广告软文是什么意思
  • 做网站优化步骤制作app平台需要多少钱
  • 龙岗网站设计讯息b2b电商平台
  • 做视频网站审核编辑有假么武汉seo招聘网
  • 专做美容师招聘网站我想注册一个网站怎么注册
  • 电商平台正在建设中网站页面提示深圳搜索引擎优化seo
  • 如何在网站上显示百度权重百度一下电脑版首页网址
  • 做彩票网站代理违法吗青岛seo杭州厂商
  • 六安做网站公司百度推广一天费用200
  • 东莞市微信网站建设品牌百度应用商店下载安装
  • 做旅游网站的项目背景产品营销策划方案怎么做
  • 张家港网站设计制作专业的seo搜索引擎优化培训
  • 学做彩票网站经典营销案例
  • 重庆做网站建设公司哪家好在线外链
  • wordpress 图片集seo网站优化方案摘要
  • 网站qq联系怎么做专业软文平台