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

培训网站天津网站建设电焊机

培训网站,天津网站建设电焊机,做网站设计和推广,软文广告平台原文地址#xff1a;cmsblogs.com/?p2442 ThreadLocal介绍 ThreadLocal提供了一种解决多线程环境下成员变量的问题#xff0c;但是它并不是解决多线程共享变量的问题。那么ThreadLocal到底是什么呢#xff1f; API是这样介绍的#xff1a;This class provides thread-loca… 原文地址cmsblogs.com/?p2442 ThreadLocal介绍 ThreadLocal提供了一种解决多线程环境下成员变量的问题但是它并不是解决多线程共享变量的问题。那么ThreadLocal到底是什么呢 API是这样介绍的This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID). 该类提供了线程局部thread-local变量。这些变量不同于普通对应物因为访问某个变量通过其get或set方法的每个线程都有自己的局部变量它独立于变量的初始化副本。ThreadLocal实例通常是类中的private static字段它们希望将状态与某一个线程例如用户ID或事务ID相关联。 ThreadLocal与线程同步机制不同线程同步机制是多个线程共享同一个变量而ThreadLocal为了每一个线程创建一个单独的变量副本故而每个线程都可以独立地改变自己所拥有的变量副本而不会影响其他线程所对应的副本。 ThreadLocal使用示例代码如下 public class SeqCount {private static ThreadLocalInteger seqCount new ThreadLocalInteger() {// 实现initialValue()public Integer initialValue() {return 0;}};public int nextSeq() {seqCount.set(seqCount.get() 1);return seqCount.get();}public static void main(String[] args) {SeqCount seqCount new SeqCount();SeqThread thread1 new SeqThread(seqCount);SeqThread thread2 new SeqThread(seqCount);SeqThread thread3 new SeqThread(seqCount);SeqThread thread4 new SeqThread(seqCount);thread1.start();thread2.start();thread3.start();thread4.start();}private static class SeqThread extends Thread {private SeqCount seqCount;SeqThread(SeqCount seqCount) {this.seqCount seqCount;}public void run() {for (int i 0; i 3; i) {System.out.println(Thread.currentThread().getName() seqCount : seqCount.nextSeq());}}} } 复制代码ThreadLocal实现原理 ThreadLocal的实现是这样的每个Thread维护一个ThreadLocalMap映射表这个映射表的key是 ThreadLocal实例本身value是真正需要存储的Object。 也就是说ThreadLocal本身并不存储值它只是作为一个key来让线程从ThreadLocalMap获取 value。值得注意的是图中的虚线表示ThreadLocalMap是使用ThreadLocal的弱引用作为Key的弱引用的对象在GC时会被回收。 ThreadLocal源码分析 ThreadLocalMap 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); } 复制代码由上可知ThreadLocalMap其内部利用Entry来实现key-value的存储如下 static class Entry extends WeakReferenceThreadLocal? {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal? k, Object v) {super(k);value v;} } 复制代码可以看出Entry的key就是ThreadLocal而value就是值。同时Entry也继承WeakReference所以说Entry所对应keyThreadLocal实例的引用为一个弱引用。 接下来看看ThreadLocalMap最核心的方法set(ThreadLocal key, Object value)、getEntry()方法。 1、set(ThreadLocal? key, Object value) private void set(ThreadLocal? key, Object value) {Entry[] tab table;int len tab.length;// 根据 ThreadLocal 的散列值查找对应元素在数组中的位置int i key.threadLocalHashCode (len-1);for (Entry e tab[i];e ! null;e tab[i nextIndex(i, len)]) {ThreadLocal? k e.get();// key 存在直接覆盖if (k key) {e.value value;return;}// key null但是存在值因为此处的e ! null说明之前的ThreadLocal对象已经被回收if (k null) {// 用新元素替换陈旧的元素replaceStaleEntry(key, value, i);return;}}// ThreadLocal对应的key实例不存在则创建tab[i] new Entry(key, value);int sz size;// cleanSomeSlots 清楚陈旧的Entrykey null// 如果没有清理陈旧的 Entry 并且数组中的元素大于了阈值则进行 rehashif (!cleanSomeSlots(i, sz) sz threshold)rehash();}复制代码set()操作除了存储元素外还有一个很重要的作用就是replaceStaleEntry()和cleanSomeSlots()这两个方法可以清除掉key null 的实例防止内存泄漏。在set()方法中还有一个变量很重要threadLocalHashCode定义如下 private final int threadLocalHashCode nextHashCode(); 复制代码从名字上面我们可以看出threadLocalHashCode应该是ThreadLocal的散列值定义为final表示ThreadLocal一旦创建其散列值就已经确定了生成过程则是调用nextHashCode() private static AtomicInteger nextHashCode new AtomicInteger();private static final int HASH_INCREMENT 0x61c88647;private static int nextHashCode() {return nextHashCode.getAndAdd(HASH_INCREMENT); } 复制代码nextHashCode表示分配下一个ThreadLocal实例的threadLocalHashCode的值HASH_INCREMENT则表示分配两个ThradLocal实例的threadLocalHashCode的增量。 2、getEntry() 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); } 复制代码采用了开放定址法所以当前key的散列值和元素在数组的索引并不是完全对应的首先取一个探测数key的散列值如果所对应的key就是我们所要找的元素则返回否则调用getEntryAfterMiss()。 private Entry getEntryAfterMiss(ThreadLocal? key, int i, Entry e) {Entry[] tab table;int len tab.length;while (e ! null) {ThreadLocal? k e.get();if (k key)return e;// 当key null时调用了expungeStaleEntry()方法该方法用于处理key null// 有利于GC回收能够有效地避免内存泄漏。if (k null)expungeStaleEntry(i);elsei nextIndex(i, len);e tab[i];}return null;} 复制代码ThreadLocal核心方法 set(T value)设置当前线程的线程局部变量的值 public void set(T value) {Thread t Thread.currentThread();ThreadLocalMap map getMap(t);if (map ! null)map.set(this, value);elsecreateMap(t, value); }void createMap(Thread t, T firstValue) {t.threadLocals new ThreadLocalMap(this, firstValue); } 复制代码获取当前线程所对应的ThreadLocalMap如果不为空则调用ThreadLocalMap的set()方法key就是当前ThreadLocal如果不存在则调用createMap()方法创建。 get()返回当前线程所对应的线程变量 public T get() {Thread t Thread.currentThread();ThreadLocalMap map getMap(t);if (map ! null) {ThreadLocalMap.Entry e map.getEntry(this);if (e ! null) {SuppressWarnings(unchecked)T result (T)e.value;return result;}}// 如果ThreadLocalMap不存在返回初始值。return setInitialValue(); } 复制代码首先通过当前线程获取所对应的成员变量ThreadLocalMap然后通过ThreadLocalMap获取当前ThreadLocal的Entry最后通过所获取的Entry获取目标值result。 initialValue()返回该线程局部变量的初始值 protected T initialValue() {return null; } 复制代码这个方法将在一个线程第一次使用get方法访问变量时被调用除非线程先前调用了set方法在这种情况下线程不会调用initialValue方法。通常情况下每个线程最多调用一次此方法但在后续调用remove和get时可能会再次调用此方法。 默认实现返回null如果程序员希望线程局部变量具有非null的初始值则必须对ThreadLocal进行子类化并重写此方法。 remove()将当前线程局部变量的值删除 public void remove() {ThreadLocalMap m getMap(Thread.currentThread());if (m ! null)m.remove(this); } 复制代码该方法的目的是减少内存的占用。当然我们不需要显示调用该方法因为一个线程结束后它所对应的局部变量就会被垃圾回收。 ThreadLocal为什么会内存泄漏 ThreadLocalMap使用ThreadLocal的弱引用作为key如果一个ThreadLocal没有外部强引用来引用它那么系统GC的时候这个ThreadLocal势必会被回收ThreadLocalMap中就会出现key为null的Entry就没有办法访问这些key为null的Entry的value。如果当前线程再迟迟不结束的话这些key为null的Entry的value就会一直存在一条强引用链Thread Ref - Thread - ThreaLocalMap - Entry - value永远无法回收造成内存泄漏。 其实ThreadLocalMap的设计中已经考虑到这种情况也加上了一些防护措施在ThreadLocal的get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value。 但是这些被动的预防措施并不能保证不会内存泄漏 使用static的ThreadLocal延长了ThreadLocal的生命周期可能导致的内存泄漏。 分配使用了ThreadLocal又不再调用get(),set(),remove()方法那么就会导致内存泄漏。 ThreadLocal内存泄漏的根源是由于ThreadLocalMap的生命周期跟Thread一样长如果没有手动删除对应key就会导致内存泄漏而不是因为弱引用。 理解了ThreadLocal内存泄漏的前因后果那么怎么避免内存泄漏呢 每次使用完ThreadLocal都调用它的remove()方法清除数据。在使用线程池的情况下没有及时清理ThreadLocal不仅是内存泄漏的问题更严重的是可能导致业务逻辑出现问题。所以使用ThreadLocal就跟加锁完要解锁一样用完就清理。 参考资料 【死磕Java并发】—–深入分析ThreadLocal 深入分析 ThreadLocal 内存泄漏问题 如果读完觉得有收获的话欢迎点赞、关注、加公众号【牛觅技术】查阅更多精彩历史
http://wiki.neutronadmin.com/news/41612/

相关文章:

  • 精通网站建设pdf网站起域名原则
  • 专业建站网站鹤壁市城乡一体化示范区网站
  • 建设黑彩网站商城网站建设模板
  • 购物券网站怎么做官网定制公司
  • 北京官方网站怎么做互联网推广策略
  • 胶州企业网站设计网站备案需要多少时间
  • 网站可以制作ios创建网站花钱吗
  • 高端网站制作软件wordpress投票模板
  • 做互联网网站待遇做AI免费网站
  • 广州网站制作公司多少钱seo算法培训
  • 肇庆cms建站系统小程序制作开发培训
  • 点评网站模板网站怎样注册备案
  • 一个专做里番的网站东莞网上推广怎么做
  • 事业单位网站设计2024年新手机上市时间表
  • 做一个网上商城网站建设费用多少钱自己做网站新手入门
  • 网站开发调查表wordpress 跳转 代理
  • 合肥网站设计高端公司网站架设
  • 厦门网站制作系统网站 设计公司 温州
  • 怎样进行网站推广传媒公司名字大全免费
  • 怎么推广自己的网站?delphi网站开发教程
  • 网站顶部下拉广告专业高端网站设计首选
  • 乐清做网站建设企业培训十大热门课程
  • 响应式网站 外贸企业所得税的计算公式
  • 手机网站加速器中国商铺网
  • 电影网站建设基本流程景区协会官方网站建设
  • 网站的模块中国建筑界网官网
  • 建设网站公司怎么样红旗网站建设
  • 网站类网站开发外协机械加工订单
  • 做推广比较好的网站江苏扬州建设工程信息网站
  • 泰安千橙网站建设优化熊掌号程序员 给老婆做网站