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

网站开发 东莞深圳微信网站制作

网站开发 东莞,深圳微信网站制作,百度搜索优化,建立网站的目标在面试的时候#xff0c;ThreadLocal作为高并发常用工具经常会被问到。而面试官比较喜欢问的问题有以下两个#xff1a;1、ThreadLocal是怎么实现来保证每个线程的变量副本的。2、ThreadLocal的内存泄露是怎么产生的#xff0c;怎么避免内存泄露。首先我们来看第一个问题ThreadLocal作为高并发常用工具经常会被问到。而面试官比较喜欢问的问题有以下两个1、ThreadLocal是怎么实现来保证每个线程的变量副本的。2、ThreadLocal的内存泄露是怎么产生的怎么避免内存泄露。首先我们来看第一个问题实际上这个问题主要是想考察候选人是否有阅读过ThreadLocal的源码。当然阅读源码前我们得了解ThreadLocal是怎么使用的只有使用过ThreadLocal我们才能产生对ThreadLocal是怎么做到的这样的疑问。而带着问题去阅读源码才能有一种廓然开朗的感觉。废话不多说放码过来。贴上threadlocal官方示例public class ThreadId {     // Atomic integer containing the next thread ID to be assigned private static final AtomicInteger nextId new AtomicInteger(0); // Thread local variable containing each threads ID     private static final ThreadLocal threadId          new ThreadLocal() { Override protected Integer initialValue() { return nextId.getAndIncrement();         }     };      // Returns the current threads unique ID, assigning it if necessary     public static int get() {         return threadId.get();     } }上面代码主要通过从threadlocal得到该线程的id如果当前线程没有的时候则生成一个。通过上述例子我们会有如下疑问1、为什么ThreadLocal可以获取到当前线程的变量副本   - 猜测ThreadLocal内部有一个Map对象(Map)key为线程对象value为我们存储的变量。带着上述疑问我们看下ThreadLocal的源码实现。先看set方法。public class ThreadLocal { public void set(T value) { Thread t Thread.currentThread(); ThreadLocalMap map getMap(t); if (map ! null) map.set(this, value); else createMap(t, value); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; } void createMap(Thread t, T firstValue) { t.threadLocals new ThreadLocalMap(this, firstValue); } }从getMap方法得知ThreadLocal内部确实存在一个Map(ThreadLocalMap)只不过该map是线程的一个内部属性。而从createMap方法我们得知ThreadLocalMap是以ThreadLocal作为key我们提供的值为value。进入到Thread类源码查看有两个ThreatLocalMap类型的属性。从注释可以看出第一个是由ThreadLocal类维护的第二个是由InheritableThreadLocal类维护的。而ThreadLocalMap是ThreadLocal的静态内部类。public class Thread implements Runnable { /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals null; /* * InheritableThreadLocal values pertaining to this thread. This map is * maintained by the InheritableThreadLocal class. */ ThreadLocal.ThreadLocalMap inheritableThreadLocals null; ......}ThreadLocalMap内部维护的是一个Entry数组而Entry数组继承WeakReference。我们知道weakReference是当jvm内存不足时会回收只有WeakReference引用的对象。而ThreadLocalMap这样做的意图是为什么呢这个问题会跟第二个疑问一起解答。static class ThreadLocalMap { static class Entry extends WeakReferenceThreadLocal { /** The value associated with this ThreadLocal. */            Object value; Entry(ThreadLocal k, Object v) { super(k); value v; } } private Entry[] table; ......}如果是第一次看ThreadLocal源码的话看到这里可能觉得有点绕画个图理清一下关系。首先创建了一个ThreadLocal对象在一个线程里调用ThreadLocal的get方法假设是第一次调用得到了null这时我们在通过数据库查询得到value并调用set方法设置了进去如果该Thread的threadlocals属性没有被初始化过则会执行ThreadLocal的createMap方法。下次该线程执行调用get方法时就会从得到先set进去的值。上面没有贴出get方法的代码但是我们可以猜测出是通Thread.currentThread().threadLocals.get(this).value获取到值的。这里就不贴出来了。第一个疑问解决了现在看下面试问到的第二个问题。ThreadLocal的内存泄露是怎么产生的怎么避免内存泄露网上搜ThreadLocal内存泄露很多文章都会说到ThreadLocal的ThreadLocalMap的Entry数组继承的是WeakReference而WeakReference会在jvm内存不足是回收引用。当thread常驻或者使用线程池时核心线程常驻thread未回收而ThreadLocal被回收但是value又是强引用因此不会被回收而存在内存泄露。当我看了网上形形色色的文章都是这样的描述后仍然云里雾里而且也不符合我们实际的使用场景。产生这个问题主要有以下几个方面的原因一方面是我们虽然使用过ThreadLocal但是对ThreadLocal会导致的内存泄露问题场景不熟悉导致的。另一方面是我们虽然没有正确使用ThreadLocal但是内存泄露问题并不足于导致OOM(或者说存在内存泄露的问题但是并不致命)。以下分几个场景讨论ThreadLocal内存泄露的问题。场景一一个请求对应一个新线程这种场景下使用ThreadLocalThread里的threatLocals变量会随着线程的销毁而销毁自然也就不存在内存泄露的问题了。场景二存在强引用的几个ThreadLocal  少量核心线程数的线程池使用ThreadLocal的场景中我们多数都是定义为static类变量但是并不是意味着只有static修饰才是强引用只要有被别的类进行强引用的都算只是定义为static类变量是我们比较常用的场景(参照官方例子)。由于ThreadLocal存在外部强引用因此ThreadLocalMap的key不会出现null的情况而少量核心线程数意味着变量副本不会很多。因此内存泄露的量为 s  coreThreadNums * (objectSize1  objectSize2  ...  objectSizeX) X  threadLocalNums 因此当线程池核心线程数不多threadLocal数量不多和threadLocal变量副本不大时虽然存在内存泄露但是却不足于导致OOM。但是当threadLocal变量副本比较大时内存泄露的情况就会严重导致OOM的可能性加剧了。场景三无强引用的几个ThreadLocal  少量核心线程数的线程池解析跟场景二类似只是threadlocalmap的key会变为null但是value却不会被回收。因此还是存在内存泄露的情况。场景二和场景三还有一个前提条件是线程池运行的task对threadlocal的使用是一次性的。因为当调用threadLocal的set、get和remove方法时会将threadlocalmap里key为null的entry的value释放掉(实际调用的是ThreadLocalMap的expungeStaleEntry方法感兴趣的去看源码)。当然使用了强引用的ThreadLocal是享受不到该好处的。场景四无强引用的大量ThreadLocal  少量核心线程数的线程池跟场景三类似只是threadLocal作为threadlocal的key占用的内存稍微大了一点场景五存在强引用的少量ThreadLocal  大量核心线程数的线程池解析跟场景二类型由于每个常驻线程都有副本因此内存泄露的情况加剧了。假设1000个核心线程每个变量副本大小为1m2个threadlocal则导致2g的内存泄露。场景六无强引用的少量ThreadLocal  大量核心线程数的线程池跟场景四类似。场景七无强引用ThreadLocal  大量常驻线程的线程池服务于特定任务这里的特定任务是指任务里都会用到threadlocal get或set 方法的任务由于每次都调用了get或set方法threadlocalmap会清理掉key为null的但是当没有任务执行时threadlocalmap的value仍然不会被回收存在内存泄露。import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ThreadLocalTests { public static void main(String[] args) throws Exception{ final ThreadLocal threadLocal1 new ThreadLocal(); final ThreadLocal threadLocal2 new ThreadLocal(); final ExecutorService threadPool Executors.newFixedThreadPool(1000); Thread.sleep(10000L); for (int i 0; i 1000; i){ threadPool.submit(() - { byte[] bytes new byte[1024 * 1024]; threadLocal1.set(bytes); }); threadPool.submit(() - { byte[] bytes new byte[1024 * 1024]; threadLocal2.set(bytes); }); } }}import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ThreadLocalTests { public static void main(String[] args) throws Exception{ final ThreadLocal threadLocal1 new ThreadLocal(); final ThreadLocal threadLocal2 new ThreadLocal(); final ExecutorService threadPool Executors.newFixedThreadPool(1000); Thread.sleep(10000L); for (int i 0; i 1000; i){ threadPool.submit(() - { byte[] bytes new byte[1024 * 1024]; threadLocal1.set(bytes);                threadLocal1.remove(); }); threadPool.submit(() - { byte[] bytes new byte[1024 * 1024]; threadLocal2.set(bytes); threadLocal2.remove(); }); } }}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; } }}public void clear() { this.referent null;}其他场景就不讨论了可自行扩展。这里做个总结。无论threadlocal数量多少和常驻线程数量的多少都会导致内存泄露的问题只是严重程度不同罢了。那怎样才是使用ThreadLocal的正确姿势而不会导致内存泄露呢这里举例zuul的requestContext例子。zuul的requestContextpublic class RequestContext extends ConcurrentHashMapString, Object { protected static final ThreadLocal extends RequestContext threadLocal new ThreadLocal() { Override protected RequestContext initialValue() { try { return contextClass.newInstance(); } catch (Throwable e) { throw new RuntimeException(e); } } }; /** * unsets the threadLocal context. Done at the end of the request. */ public void unset() { threadLocal.remove(); }}public class ZuulServlet extends HttpServlet { Override public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException { try { init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse); // Marks this request as having passed through the Zuul engine, as opposed to servlets // explicitly bound in web.xml, for which requests will not have the same data attached RequestContext context RequestContext.getCurrentContext(); context.setZuulEngineRan(); try { preRoute(); } catch (ZuulException e) { error(e); postRoute(); return; } try { route(); } catch (ZuulException e) { error(e); postRoute(); return; } try { postRoute(); } catch (ZuulException e) { error(e); return; } } catch (Throwable e) { error(new ZuulException(e, 500, UNHANDLED_EXCEPTION_ e.getClass().getName())); } finally {            // 实际执行threadlocal的清理方法 RequestContext.getCurrentContext().unset(); } }}只要在每次用完threadlocal后执行threadlocal的remove方法就可以清除掉变量副本这样就不会产生内存泄露了。ThreadLocalMap的remove方法调用ThreadLocal的remove方法实际上是执行了ThreadLocalMap的remove方法。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; } } }
http://www.pierceye.com/news/392818/

相关文章:

  • 卖汽车的网站怎么做百度图片点击变网站是怎么做的
  • 可以做英文教师的网站桂阳网站建设
  • 常州网站建设外包WordPress cos媒体库
  • 教着做美食的网站wordpress 无法安装主题
  • 只选设计师的网站牛商网是干什么的
  • 网站设计中新闻版块怎么做乐清新闻联播
  • 网站开发维护员挣钱吗全球电子商务公司排行
  • 祥云县住房和城乡建设网站百度热议排名软件
  • 网站开发攻克时间网站模版制作教程
  • 遵义一般做一个网站需要多少钱菲斯曼售后服务中心
  • 教务系统网站建设模板下载东莞企业高端网站建设
  • 淮南建设公司网站网站建设对教育解决方案
  • 泰兴建设局网站wordpress资料图片不显示
  • 外贸推广免费网站图片 网站源码
  • 博客推广那个网站列好邢台网红桥
  • 艺之都网站建设微信app开发腾讯视频分享到wordpress
  • 洛阳最好的做网站的公司哪家好建网站需要哪些文件夹
  • 舟山企业网站建设导出wordpress用户
  • 肇庆新农村建设内容在哪个网站有关天猫网站开发的论文
  • 网站建设代码生成器php网站开发专员招聘
  • 视频教学网站cms陕西网站备案查询
  • 湖州网站设计浙北数据wordpress自定义搜索页面
  • 昆明公司网站开发流线型的网站建设
  • 南京建设网站企业泊头市建设网站
  • 前端跟后端哪个就业难北京网站建设seo优化
  • 简述网站开发建设的基本流程做一个京东这样的网站需要多少钱
  • 与通信工程专业做项目的网站微信开发显示wordpress
  • 自己做链接网站萍乡做网站哪家好
  • 做网站最适合用多大的图片医院 网站建设 新闻
  • 网站开发职业分析产品展示的手机网站