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

机票特价网站建设优化关键词推广

机票特价网站建设,优化关键词推广,防止网站被攻击,wordpress设置语言目录一. ThreadLocal的内部结构1 常见的误解2 现在的设计3 这样设计的好处二. ThreadLocal的核心方法源码1 set方法2 get方法3 remove方法**4 initialValue方法**三. ThreadLocalMap源码分析1 基本结构2 弱引用和内存泄漏3 hash冲突的解决一. ThreadLocal的内部结构 ​ 通过之… 目录一. ThreadLocal的内部结构1 常见的误解2 现在的设计3 这样设计的好处二. ThreadLocal的核心方法源码1 set方法2 get方法3 remove方法**4 initialValue方法**三. ThreadLocalMap源码分析1 基本结构2 弱引用和内存泄漏3 hash冲突的解决一. ThreadLocal的内部结构 ​ 通过之前一篇文章的学习我们对ThreadLocal的作用有了一定的认识。现在我们一起来看一下ThreadLocal的内部结构探究它能够实现线程数据隔离的原理。 1 常见的误解 ​ 如果我们不去看源代码的话可能会猜测ThreadLocal是这样子设计的每个ThreadLocal都创建一个Map然后用线程作为Map的key要存储的局部变量作为Map的value这样就能达到各个线程的局部变量隔离的效果。这是最简单的设计方法JDK最早期的ThreadLocal 确实是这样设计的但现在早已不是了。 2 现在的设计 ​ 但是JDK后面优化了设计方案在JDK8中 ThreadLocal的设计是每个Thread维护一个ThreadLocalMap这个Map的key是ThreadLocal实例本身value才是真正要存储的值Object。 具体的过程是这样的 ​ 1 每个Thread线程内部都有一个Map (ThreadLocalMap) ​ 2 Map里面存储ThreadLocal对象key和线程的变量副本value ​ 3Thread内部的Map是由ThreadLocal维护的由ThreadLocal负责向map获取和设置线程的变量值。 ​ 4对于不同的线程每次获取副本值时别的线程并不能获取到当前线程的副本值形成了副本的隔离互不干扰。 3 这样设计的好处 ​ 这个设计与我们一开始说的设计刚好相反这样设计有如下两个优势 1 这样设计之后每个Map存储的Entry数量就会变少。因为之前的存储数量由Thread的数量决定现在是由ThreadLocal的数量决定。在实际运用当中往往ThreadLocal的数量要少于Thread的数量。 2 当Thread销毁之后对应的ThreadLocalMap也会随之销毁能减少内存的使用。 二. ThreadLocal的核心方法源码 ​基于ThreadLocal的内部结构我们继续分析它的核心方法源码更深入的了解其操作原理。 除了构造方法之外 ThreadLocal对外暴露的方法有以下4个 方法声明描述protected T initialValue()返回当前线程局部变量的初始值public void set( T value)设置当前线程绑定的局部变量public T get()获取当前线程绑定的局部变量public void remove()移除当前线程绑定的局部变量 ​ 以下是这4个方法的详细源码分析(为了保证思路清晰, ThreadLocalMap部分暂时不展开,下一个知识点详解) 1 set方法 1 ) 源码和对应的中文注释 /*** 设置当前线程对应的ThreadLocal的值** param value 将要保存在当前线程对应的ThreadLocal的值*/public void set(T value) {// 获取当前线程对象Thread t Thread.currentThread();// 获取此线程对象中维护的ThreadLocalMap对象ThreadLocalMap map getMap(t);// 判断map是否存在if (map ! null)// 存在则调用map.set设置此实体entrymap.set(this, value);else// 1当前线程Thread 不存在ThreadLocalMap对象// 2则调用createMap进行ThreadLocalMap对象的初始化// 3并将 t(当前线程)和value(t对应的值)作为第一个entry存放至ThreadLocalMap中createMap(t, value);}/*** 获取当前线程Thread对应维护的ThreadLocalMap * * param t the current thread 当前线程* return the map 对应维护的ThreadLocalMap */ThreadLocalMap getMap(Thread t) {return t.threadLocals;}/***创建当前线程Thread对应维护的ThreadLocalMap ** param t 当前线程* param firstValue 存放到map中第一个entry的值*/void createMap(Thread t, T firstValue) {//这里的this是调用此方法的threadLocalt.threadLocals new ThreadLocalMap(this, firstValue);}2 ) 代码执行流程 ​ A. 首先获取当前线程并根据当前线程获取一个Map ​ B. 如果获取的Map不为空则将参数设置到Map中当前ThreadLocal的引用作为key ​ C. 如果Map为空则给该线程创建 Map并设置初始值 2 get方法 1 ) 源码和对应的中文注释 /*** 返回当前线程中保存ThreadLocal的值* 如果当前线程没有此ThreadLocal变量* 则它会通过调用{link #initialValue} 方法进行初始化值** return 返回当前线程对应此ThreadLocal的值*/public T get() {// 获取当前线程对象Thread t Thread.currentThread();// 获取此线程对象中维护的ThreadLocalMap对象ThreadLocalMap map getMap(t);// 如果此map存在if (map ! null) {// 以当前的ThreadLocal 为 key调用getEntry获取对应的存储实体eThreadLocalMap.Entry e map.getEntry(this);// 对e进行判空 if (e ! null) {SuppressWarnings(unchecked)// 获取存储实体 e 对应的 value值// 即为我们想要的当前线程对应此ThreadLocal的值T result (T)e.value;return result;}}/*初始化 : 有两种情况有执行当前代码第一种情况: map不存在表示此线程没有维护的ThreadLocalMap对象第二种情况: map存在, 但是没有与当前ThreadLocal关联的entry*/return setInitialValue();}/*** 初始化** return the initial value 初始化后的值*/private T setInitialValue() {// 调用initialValue获取初始化的值// 此方法可以被子类重写, 如果不重写默认返回nullT value initialValue();// 获取当前线程对象Thread t Thread.currentThread();// 获取此线程对象中维护的ThreadLocalMap对象ThreadLocalMap map getMap(t);// 判断map是否存在if (map ! null)// 存在则调用map.set设置此实体entrymap.set(this, value);else// 1当前线程Thread 不存在ThreadLocalMap对象// 2则调用createMap进行ThreadLocalMap对象的初始化// 3并将 t(当前线程)和value(t对应的值)作为第一个entry存放至ThreadLocalMap中createMap(t, value);// 返回设置的值valuereturn value;}2 ) 代码执行流程 ​ A. 首先获取当前线程, 根据当前线程获取一个Map ​ B. 如果获取的Map不为空则在Map中以ThreadLocal的引用作为key来在Map中获取对应的Entrye否则转到D ​ C. 如果e不为null则返回e.value否则转到D ​ D. Map为空或者e为空则通过initialValue函数获取初始值value然后用ThreadLocal的引用和value作为firstKey和firstValue创建一个新的Map 总结: 先获取当前线程的 ThreadLocalMap 变量如果存在则返回值不存在则创建并返回初始值。 3 remove方法 1 ) 源码和对应的中文注释 /*** 删除当前线程中保存的ThreadLocal对应的实体entry*/public void remove() {// 获取当前线程对象中维护的ThreadLocalMap对象ThreadLocalMap m getMap(Thread.currentThread());// 如果此map存在if (m ! null)// 存在则调用map.remove// 以当前ThreadLocal为key删除对应的实体entrym.remove(this);}2 ) 代码执行流程 ​ A. 首先获取当前线程并根据当前线程获取一个Map ​ B. 如果获取的Map不为空则移除当前ThreadLocal对象对应的entry 4 initialValue方法 /*** 返回当前线程对应的ThreadLocal的初始值* 此方法的第一次调用发生在当线程通过get方法访问此线程的ThreadLocal值时* 除非线程先调用了set方法在这种情况下initialValue 才不会被这个线程调用。* 通常情况下每个线程最多调用一次这个方法。** p这个方法仅仅简单的返回null {code null};* 如果程序员想ThreadLocal线程局部变量有一个除null以外的初始值* 必须通过子类继承{code ThreadLocal} 的方式去重写此方法* 通常, 可以通过匿名内部类的方式实现** return 当前ThreadLocal的初始值*/ protected T initialValue() {return null; } ​ 此方法的作用是 返回该线程局部变量的初始值。 1 这个方法是一个延迟调用方法从上面的代码我们得知在set方法还未调用而先调用了get方法时才执行并且仅执行1次。 2这个方法缺省实现直接返回一个null。 3如果想要一个除null之外的初始值可以重写此方法。备注 该方法是一个protected的方法显然是为了让子类覆盖而设计的 三. ThreadLocalMap源码分析 ​ 在分析ThreadLocal方法的时候我们了解到ThreadLocal的操作实际上是围绕ThreadLocalMap展开的。ThreadLocalMap的源码相对比较复杂, 我们从以下三个方面进行讨论。 1 基本结构 ​ ThreadLocalMap是ThreadLocal的内部类没有实现Map接口用独立的方式实现了Map的功能其内部的Entry也是独立实现。 1 成员变量 /*** 初始容量 —— 必须是2的整次幂*/private static final int INITIAL_CAPACITY 16;/*** 存放数据的tableEntry类的定义在下面分析* 同样数组长度必须是2的整次幂。*/private Entry[] table;/*** 数组里面entrys的个数可以用于判断table当前使用量是否超过阈值。*/private int size 0;/*** 进行扩容的阈值表使用量大于它的时候进行扩容。*/private int threshold; // Default to 0 ​ 跟HashMap类似INITIAL_CAPACITY代表这个Map的初始容量table 是一个Entry 类型的数组用于存储数据size 代表表中的存储数目 threshold 代表需要扩容时对应 size 的阈值。 2 存储结构 - Entry /** Entry继承WeakReference并且用ThreadLocal作为key.* 如果key为null(entry.get() null)意味着key不再被引用* 因此这时候entry也可以从table中清除。*/ static class Entry extends WeakReferenceThreadLocal? {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal? k, Object v) {super(k);value v;} }​ 在ThreadLocalMap中也是用Entry来保存K-V结构数据的。不过Entry中的key只能是ThreadLocal对象这点在构造方法中已经限定死了。 ​ 另外Entry继承WeakReference也就是keyThreadLocal是弱引用其目的是将ThreadLocal对象的生命周期和线程生命周期解绑。 2 弱引用和内存泄漏 ​ 有些程序员在使用ThreadLocal的过程中会发现有内存泄漏的情况发生就猜测这个内存泄漏跟Entry中使用了弱引用的key有关系。这个理解其实是不对的。 ​ 我们先来回顾这个问题中涉及的几个名词概念再来分析问题。 1 内存泄漏相关概念 Memory overflow:内存溢出没有足够的内存提供申请者使用。Memory leak: 内存泄漏是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放造成系统内存的浪费导致程序运行速度减慢甚至系统崩溃等严重后果。内存泄漏的堆积终将导致内存溢出。 2 弱引用相关概念 ​ Java中的引用有4种类型 强、软、弱、虚。当前这个问题主要涉及到强引用和弱引用 ​ 强引用“Strong” Reference就是我们最常见的普通对象引用只要还有强引用指向一个对象就能表明对象还“活着”垃圾回收器就不会回收这种对象。 ​ 弱引用WeakReference垃圾回收器一旦发现了只具有弱引用的对象不管当前内存空间足够与否都会回收它的内存。 3 如果key使用强引用 ​ 假设ThreadLocalMap中的key使用了强引用那么会出现内存泄漏吗 ​ 此时ThreadLocal的内存图实线表示强引用如下 ​ 假设在业务代码中使用完ThreadLocal threadLocal Ref被回收了。 ​ 但是因为threadLocalMap的Entry强引用了threadLocal造成threadLocal无法被回收。 ​ 在没有手动删除这个Entry以及CurrentThread依然运行的前提下始终有强引用链 threadRef-currentThread-threadLocalMap-entryEntry就不会被回收Entry中包括了ThreadLocal实例和value导致Entry内存泄漏。 ​ 也就是说ThreadLocalMap中的key使用了强引用 是无法完全避免内存泄漏的。 5如果key使用弱引用 ​ 那么ThreadLocalMap中的key使用了弱引用会出现内存泄漏吗 ​ 此时ThreadLocal的内存图实线表示强引用虚线表示弱引用如下 ​ ​ 同样假设在业务代码中使用完ThreadLocal threadLocal Ref被回收了。 ​ 由于ThreadLocalMap只持有ThreadLocal的弱引用没有任何强引用指向threadlocal实例, 所以threadlocal就可以顺利被gc回收此时Entry中的keynull。 ​ 但是在没有手动删除这个Entry以及CurrentThread依然运行的前提下也存在有强引用链 threadRef-currentThread-threadLocalMap-entry - value value不会被回收 而这块value永远不会被访问到了导致value内存泄漏。 ​ 也就是说ThreadLocalMap中的key使用了弱引用 也有可能内存泄漏。 6出现内存泄漏的真实原因 ​ 比较以上两种情况我们就会发现内存泄漏的发生跟ThreadLocalMap中的key是否使用弱引用是没有关系的。那么内存泄漏的的真正原因是什么呢 ​ 细心的同学会发现在以上两种内存泄漏的情况中都有两个前提 1. 没有手动删除这个Entry 2. CurrentThread依然运行​ 第一点很好理解只要在使用完ThreadLocal调用其remove方法删除对应的Entry就能避免内存泄漏。 ​ 第二点稍微复杂一点 由于ThreadLocalMap是Thread的一个属性被当前线程所引用所以它的生命周期跟Thread一样长。那么在使用完ThreadLocal的使用如果当前Thread也随之执行结束ThreadLocalMap自然也会被gc回收从根源上避免了内存泄漏。 ​ 综上ThreadLocal内存泄漏的根源是由于ThreadLocalMap的生命周期跟Thread一样长如果没有手动删除对应key就会导致内存泄漏。 7 为什么使用弱引用 ​ 根据刚才的分析, 我们知道了 无论ThreadLocalMap中的key使用哪种类型引用都无法完全避免内存泄漏跟使用弱引用没有关系。 ​ 要避免内存泄漏有两种方式 使用完ThreadLocal调用其remove方法删除对应的Entry 使用完ThreadLocal当前Thread也随之运行结束 相对第一种方式第二种方式显然更不好控制特别是使用线程池的时候线程结束是不会销毁的。​ 也就是说只要记得在使用完ThreadLocal及时的调用remove无论key是强引用还是弱引用都不会有问题。那么为什么key要用弱引用呢 ​ 事实上在ThreadLocalMap中的set/getEntry方法中会对key为null也即是ThreadLocal为null进行判断如果为null的话那么是会对value置为null的。 ​ 这就意味着使用完ThreadLocalCurrentThread依然运行的前提下就算忘记调用remove方法弱引用比强引用可以多一层保障弱引用的ThreadLocal会被回收对应的value在下一次ThreadLocalMap调用set,get,remove中的任一方法的时候会被清除从而避免内存泄漏。 3 hash冲突的解决 ​ hash冲突的解决是Map中的一个重要内容。我们以hash冲突的解决为线索来研究一下ThreadLocalMap的核心源码。 1 首先从ThreadLocal的set() 方法入手 public void set(T value) {Thread t Thread.currentThread();ThreadLocal.ThreadLocalMap map getMap(t);if (map ! null)//调用了ThreadLocalMap的set方法map.set(this, value);elsecreateMap(t, value);}ThreadLocal.ThreadLocalMap getMap(Thread t) {return t.threadLocals;}void createMap(Thread t, T firstValue) {//调用了ThreadLocalMap的构造方法t.threadLocals new ThreadLocal.ThreadLocalMap(this, firstValue);}这个方法我们刚才分析过, 其作用是设置当前线程绑定的局部变量 : ​ A. 首先获取当前线程并根据当前线程获取一个Map ​ B. 如果获取的Map不为空则将参数设置到Map中当前ThreadLocal的引用作为key ​ (这里调用了ThreadLocalMap的set方法) ​ C. 如果Map为空则给该线程创建 Map并设置初始值 ​ (这里调用了ThreadLocalMap的构造方法) 这段代码有两个地方分别涉及到ThreadLocalMap的两个方法, 我们接着分析这两个方法。 2构造方法ThreadLocalMap(ThreadLocal? firstKey, Object firstValue) /** firstKey : 本ThreadLocal实例(this)* firstValue 要保存的线程本地变量*/ ThreadLocalMap(ThreadLocal? firstKey, Object firstValue) {//初始化tabletable new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY];//计算索引(重点代码int i firstKey.threadLocalHashCode (INITIAL_CAPACITY - 1);//设置值table[i] new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);size 1;//设置阈值setThreshold(INITIAL_CAPACITY);}​ 构造函数首先创建一个长度为16的Entry数组然后计算出firstKey对应的索引然后存储到table中并设置size和threshold。 ​ 重点分析 int i firstKey.threadLocalHashCode (INITIAL_CAPACITY - 1)。 a. 关于firstKey.threadLocalHashCode private final int threadLocalHashCode nextHashCode();private static int nextHashCode() {return nextHashCode.getAndAdd(HASH_INCREMENT);} //AtomicInteger是一个提供原子操作的Integer类通过线程安全的方式操作加减,适合高并发情况下的使用private static AtomicInteger nextHashCode new AtomicInteger();//特殊的hash值private static final int HASH_INCREMENT 0x61c88647;​ 这里定义了一个AtomicInteger类型每次获取当前值并加上HASH_INCREMENTHASH_INCREMENT 0x61c88647,这个值跟斐波那契数列黄金分割数有关其主要目的就是为了让哈希码能均匀的分布在2的n次方的数组里, 也就是Entry[] table中这样做可以尽量避免hash冲突。 b. 关于 (INITIAL_CAPACITY - 1) ​ 计算hash的时候里面采用了hashCode (size - 1)的算法这相当于取模运算hashCode % size的一个更高效的实现。正是因为这种算法我们要求size必须是2的整次幂这也能保证在索引不越界的前提下使得hash发生冲突的次数减小。 3 ThreadLocalMap中的set方法 private void set(ThreadLocal? key, Object value) {ThreadLocal.ThreadLocalMap.Entry[] tab table;int len tab.length;//计算索引(重点代码刚才分析过了int i key.threadLocalHashCode (len-1);/*** 使用线性探测法查找元素重点代码*/for (ThreadLocal.ThreadLocalMap.Entry e tab[i];e ! null;e tab[i nextIndex(i, len)]) {ThreadLocal? k e.get();//ThreadLocal 对应的 key 存在直接覆盖之前的值if (k key) {e.value value;return;}// key为 null但是值不为 null说明之前的 ThreadLocal 对象已经被回收了// 当前数组中的 Entry 是一个陈旧stale的元素if (k null) {//用新元素替换陈旧的元素这个方法进行了不少的垃圾清理动作防止内存泄漏replaceStaleEntry(key, value, i);return;}}//ThreadLocal对应的key不存在并且没有找到陈旧的元素则在空元素的位置创建一个新的Entry。tab[i] new Entry(key, value);int sz size;/*** cleanSomeSlots用于清除那些e.get()null的元素* 这种数据key关联的对象已经被回收所以这个Entry(table[index])可以被置null。* 如果没有清除任何entry,并且当前使用量达到了负载因子所定义(长度的2/3)那么进行 * rehash执行一次全表的扫描清理工作*/if (!cleanSomeSlots(i, sz) sz threshold)rehash(); }/*** 获取环形数组的下一个索引*/private static int nextIndex(int i, int len) {return ((i 1 len) ? i 1 : 0);} ​ 代码执行流程 A. 首先还是根据key计算出索引 i然后查找i位置上的Entry B. 若是Entry已经存在并且key等于传入的key那么这时候直接给这个Entry赋新的value值, C. 若是Entry存在但是key为null则调用replaceStaleEntry来更换这个key为空的Entry, D. 不断循环检测直到遇到为null的地方这时候要是还没在循环过程中return那么就在这个null的位置新建一个Entry并且插入同时size增加1。 ​ 最后调用cleanSomeSlots清理key为null的Entry最后返回是否清理了Entry接下来再判断sz 是否 thresgold达到了rehash的条件达到的话就会调用rehash函数执行一次全表的扫描清理。 重点分析 ThreadLocalMap使用线性探测法来解决哈希冲突的。 ​ 该方法一次探测下一个地址直到有空的地址后插入若整个空间都找不到空余的地址则产生溢出。 ​ 举个例子假设当前table长度为16也就是说如果计算出来key的hash值为14如果table[14]上已经有值并且其key与当前key不一致那么就发生了hash冲突这个时候将14加1得到15取table[15]进行判断这个时候如果还是冲突会回到0取table[0],以此类推直到可以插入。 ​ 按照上面的描述可以把Entry[] table看成一个环形数组。
http://www.dnsts.com.cn/news/164382.html

相关文章:

  • 网站的meta标签优化平台公司债务风险
  • cms网站开发实验报告郑州做系统集成的公司网站
  • 有没有免费网站制作app注册推广
  • 网站建设费用估计江苏金安建设公司网站
  • 一台电脑如何做网站网站建设教学视频教程
  • 给网站网站做推广wordpress制作主题
  • 企业网站备案不通过天猫国际采取的跨境电商网络营销方式
  • 网站建设 模版企业所得税优惠政策最新2024
  • 全球购物网站排名云服务器可以做网站吗
  • 公司注册地址怎么查哈尔滨关键词优化价格
  • 吴中区网站设计公司天津网站建设
  • 中小型企业网站开发优秀企业vi设计案例
  • 信息化建设 公司网站一家专门做母婴的网站
  • 韩国男女做那个视频网站温州网站设计工作室
  • 分类目录网站大全汕头seo优化公司
  • 手机网站建站软件网站要做手机版怎么做
  • 我想给别人做网站网页首页设计模板图片
  • 广州网站制作公司排名学做网站论坛教程下载
  • 新增网站湖南长沙地图
  • 长沙品牌网站制作服务报价淘宝做网站的店
  • 网站建设客户需求分析调研表设计师经常看的app
  • 临沂网站建设方案报价做文献综述的文章用什么网站
  • 做门户网站用什么服务器wordpress文章收录后显示
  • 三亚市住房和城乡建设厅网站wordpress地图
  • app网站建设 - 百度wordpress邮箱链接无效
  • 昌平网站开发公司青岛百度seo代理
  • 跨境o2o网站建设方案本地安装wordpress账户是什么
  • 二手房网站建设实体门店管理系统
  • 企业建设网站的目的是建设微信商城网站
  • 做哪个外贸网站不用交费网站开发微信支付接入