您的位置:澳门皇家赌场真人在线 > 皇家赌场游戏 > 线程密封之ThreadLocal源码详解,知识点速记

线程密封之ThreadLocal源码详解,知识点速记

发布时间:2019-10-04 15:43编辑:皇家赌场游戏浏览(52)

    配置JPA,application.yml

    一、线程密闭

    《Java并发编制程序实战》一书中涉嫌,“当访谈分享的可变数据时,常常须求使用同步。一种防止使用同步的措施正是不分享数据”。因而提议了“线程密封”的定义,一种常常使用线程密封的运用场景就是JDBC的Connection,通过线程密封手艺,能够把链接对象密闭在有个别线程内部,进而制止出现多个线程分享同三个链接的图景。而线程密闭总共有三类别型的呈现格局:

    1)Ad-hoc线程密封。维护线程密封性的任务由程序实现来承担,不过这种达成情势是软弱的;

    2)栈封闭。实际上通过尽量选拔部分变量的章程,防止任何线程获取数据;

    3)ThreadLocal类。通过JDK提供的ThreadLocal类,能够保障某些对象仅在线程内部被访谈,而该类正是本篇作品就要探讨的剧情。

    package okhttp3.benchmarks;public class PrimeThreads { public static void main(String[] args) { PrimeThreads pt = new PrimeThreads; //第两百五十万个素数41161739 //System.out.println(new PrimeFinder.isPrime); } public PrimeThreads(String[] arguments){ PrimeFinder[] finder = new PrimeFinder[arguments.length]; for(int i = 0;i <arguments.length;i++){ try { long count = Long.parseLong(arguments[i]); finder[i] = new PrimeFinder; System.out.println("Looking for prime " + count); }catch(NumberFormatException nfe){ System.out.println("Error: " + nfe.getMessage; } } boolean complete = false; while (!complete){ complete = true; for(int j = 0;j < finder.length;j++){ if(finder[j] == null ) continue; if(!finder[j].finished) { complete = false; }else { displayResult(finder[j]); finder[j] = null; } } try { Thread.sleep; } catch (InterruptedException ie){ //do nothing } } } private void displayResult(PrimeFinder finder) { System.out.println("Prime " + finder.target + " is " + finder.prime); }}
    

    概览

    那篇文章,大家筹划追究一下Java集合(Collections)框架中Map接口中HashMap的兑现。Map尽管是Collctions框架的一有的,但是Map并未落到实处Collection接口,而Set和List是促成Collection接口的。

    简单的讲的话,HashMap重要透过key存款和储蓄value值,并且提供了丰富,获取和操作存款和储蓄value的主意。HashMap的贯彻基于HashTable。

    图片 1HashMap内部呈现

    Key-value对在中间是以buckets的方法存款和储蓄在同步,最终造成三个表。存款和储蓄和寻觅操作的时刻是确定地点的,也正是岁月复杂度为O。

    那篇文章目前可是分涉及HashMap的平底,大家先对HashMap有个总体会认知知。

    public interface GirlRepository extends JpaRepository<Girl,Integer> { public List<Girl> findByAge(Integer age);}
    

    以下介绍一些downloader代码,这个代码的共同点是贯彻了Downloader接口。

    三、举个栗子

    SimpleDateFormat是JDK提供的,一类用于拍卖时间格式的工具,可是因为中期的完毕,导致那一个类而不是是一个线程安全的兑现,因而,在利用的时候大家会须要动用线程密闭技能来担保使用该类进度中的线程安全,在这里,大家使用了ThreadLocal,下边的兑现是行使SimpleDateFormat格式化当前时刻并出口:

    private static ThreadLocal<SimpleDateFormat> localFormatter = new ThreadLocal<SimpleDateFormat>();static { localFormatter.set(new SimpleDateFormat("yyyyMMdd"));} public static void main(String[] args) { Date now = new Date(); System.out.println(localFormatter.get().format;}
    
    package okhttp3.benchmarks;public class PrimeFinder implements Runnable{ public long target; public long prime; public boolean finished = false; private Thread runner; PrimeFinder(long inTarget) { target = inTarget; if(runner == null) { runner = new Thread; runner.start(); } } @Override public void run() { long numPrimes = 0; long candidate = 2; while ( numPrimes < target){ if (isPrime(candidate)) { numPrimes++; prime = candidate; } candidate++; } finished = true; } boolean isPrime(long checkNumber) { double root = Math.sqrt(checkNumber); for(int i =2; i <= root;i++){ if(checkNumber % i == 0) return false; } return true; } public static void main(String[] args) { System.out.println(new PrimeFinder.isPrime; }}
    

    HashMap性能

    HashMap的性情首要有五个参数影响,开始容积和负载因子。开端体量为Map底层桶数组的长度,负载因子为当桶体积的长度为多大的时候,重新开垦新的空中。

     int threshold; final float loadFactor;
    

    暗许的起来容积为16,私下认可的载荷因子为0.75. 大家也可以自定义它们的值。

    Map<String,String> hashMapWithCapacity=new HashMap<>;Map<String,String> hashMapWithCapacityAndLF=new HashMap<>;
    

    开班容积:

    大的初叶容积用于条约数很多,不过小量迭代(iteration)小的发端体量用于条目数相当少,不过一再迭代(iteration)

    负载因子:0.75是三个很折衷的方案了。在大家开端化HashMap的时候,初步体量和负载因子都应有考虑在内,比如为了收缩重复hash的操作,初阶体量乘以负载因子应该不仅仅能积攒的最大条文数,那样就不会生出再一次hash的操作。

    异常

    @ControllerAdvicepublic class ExceptionHandle { @ExceptionHandler(value = Exception.class) @ResponseBody public Result handle(Exception e) { if (e instanceof GirlException) { GirlException girlException = (GirlException) e; return ResultUtil.error(girlException.getCode(), girlException.getMessage; } else { return ResultUtil.error(-1, "未知错误"); } }}
    

    回想Spring里要承袭RuntimeException,能够事务回滚。而Exception不会。

    public class GirlException extends RuntimeException { private Integer code; public GirlException(Integer code, String msg) { super; this.code = code; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; }}
    
    • HttpClientDownloader那几个是用开源包apache httpclient实现的,代码就越是从简高尚了。

    简书江溢乔恩ny,转发请评释原再次创下处,多谢!

    图片 2

    HashMap中的集结视图

    HashMap提供了两种办法,让我们能够把key和value作为任何集合来行使。

    Set<K> keys = map.keySet()Collection<V> values = map.values()Set<Entry<K, V>> entries = map.entrySet();
    

    注意: 在iteators创造完成后,对map的其余协会修改,都会抛出二个老大。

    @Testpublic void givenIterator_whenFailsFastOnModification_thenCorrect() { Map<String, String> map = new HashMap<>(); map.put("name", "baeldung"); map.put("type", "blog"); Set<String> keys = map.keySet(); Iterator<String> it = keys.iterator(); map.remove; while (it.hasNext { String key = it.next(); }}// 会抛出java.util.ConcurrentModificationException异常
    

    HashMap中独步一时允许的修改是在iterator中移除成分。

    public void givenIterator_whenRemoveWorks_thenCorrect() { Map<String, String> map = new HashMap<>(); map.put("name", "baeldung"); map.put("type", "blog"); Set<String> keys = map.keySet(); Iterator<String> it = keys.iterator(); while (it.hasNext { it.next(); it.remove(); } assertEquals(0, map.size;}
    

    HashMap在iterator上的天性比较于LinkedHashMap和treeMap,质量相当差。最差意况下为O,n为hashmap中条指标个数。

    事务管理

    加二个注脚@Transactional

    //1、构建一个URL对象url = new URL(request.getUrl;//2、获取一个HttpURLConnection对象conn = url.openConnection();//3、一堆设置conn .setDoOutput;conn .setDoInput;conn .setRequestMethod;......//4、访问网络服务conn.connect();//5、执行成功的话,获取结果conn.getResponseCode();conn.getInputStream();
    
    何以是0x61c88647

    本条魔数的采纳与斐波那契散列有关,0x61c88647对应的十进制为1640531527。斐波那契散列的乘数能够用((1L << 31) * (Math.sqrt能够获得2654435769,就算把这么些值给转为带符号的int,则会获取-1640531527(也正是0x61c88647)。通过理论与推行,当大家用0x61c88647充任魔数累加为种种ThreadLocal分配各自的ID也正是threadLocalHashCode再与2的幂取模,拿到的结果布满很均匀。ThreadLocalMap使用的是线性探测法,均匀布满的好处在于不慢就会探测到下多少个接近的可用slot,从而确定保证成效。

    大家共同来回想一下,table对象的前奏体积是足以宽容15个对象,在set()方法的尾巴能够看见以下内容:

    // 清理无效数据后判断是否仍需扩容if (!cleanSomeSlots && sz >= threshold) rehash(); // 扩容
    

    假若当前体积大小大于阈值(threshold)后,将会发起一回扩大体积操作。

    private void rehash() { expungeStaleEntries(); if (size >= threshold - threshold / 4) resize();}
    

    在该格局中,首先尝试通透到底清理表中的无效成分,然后判别当前是还是不是仍旧高于threshold值的3/4。

    而threshold值,在作品开端的时候就已经谈起过,是当前体积大小的2/3:

    /*** 在当前容量大小超过table大小的2/3时可能会触发一次rehash操作*/private void setThreshold { threshold = len * 2 / 3;}
    

    那么大家一齐拜访resize()方法:

    /** * 成倍扩容table */private void resize() { Entry[] oldTab = table; int oldLen = oldTab.length; int newLen = oldLen * 2; // 直接倍增 Entry[] newTab = new Entry[newLen]; int count = 0; for (int j = 0; j < oldLen; ++j) { Entry e = oldTab[j]; if (e != null) { ThreadLocal k = e.get(); if (k == null) { e.value = null; // 释放无效的对象 } else { int h = k.threadLocalHashCode & (newLen - 1); while (newTab[h] != null) h = nextIndex(h, newLen); newTab[h] = e; count++; } } } setThreshold; size = count; table = newTab;}
    

    在该办法内部,首先成立三个新的表,表的深浅是原来表大小的两倍,然后再每个复制原表内容到新表中,即使发现有不行对象,则把Entry对象中对应的value引用置为NULL,方便前面垃圾收罗器对该对象的回收。

    那时候我再度贴出援引的图示:

    图片 3Entry引用

    能够看出Entry对象到ThreadLocal对象是贰个弱引用的涉嫌,而指向Object对象依旧是一个强援引的涉及,因而,尽管由于弱引用的ThreadLocal对象随着ROOT路线不可达而被垃圾采撷器清理后,然而依旧残留有Object对象,比不上时清理会存在“内部存款和储蓄器走漏”的主题素材。

    那正是说大家看看和污源搜集有关的措施:

    /** * 该方法将在set方法中被调用,在set某个值时,通过散列函数指向某个位置,然而 * 此时该位置上存在一个垃圾Entry,将会尝试使用此方法用新值覆盖旧值,不过该方 * 法还承担了“主动垃圾回收”的功能。 * * @param key 以ThreadLocal类对象作为key * @param value 通过ThreadLocal类对象找到对应的值 */private void replaceStaleEntry( ThreadLocal key, Object value,int staleSlot) { Entry[] tab = table; int len = tab.length; Entry e; // 向前扫描,查找最前的一个无效slot int slotToExpunge = staleSlot; for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex if  == null) slotToExpunge = i; // 向后遍历table,直到当前表中所指的位置是一个空值或 // 者已经找到了和ThreadLocal对象匹配的值 for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex { ThreadLocal k = e.get(); // 之前设置新值时,如果当前哈希位存在冲突, // 那么就要顺延到后面空的slot中存放。 // 既然当前哈希位原来对应的ThreadLocal对象已经 // 被回收了,那么被顺延放置的ThreadLocal对象 // 自然就要被向前调整到当前位置中去 if  { e.value = value; tab[i] = tab[staleSlot]; tab[staleSlot] = e; // swap操作 if (slotToExpunge == staleSlot) slotToExpunge = i; // 清理一波无效slot cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); return; // 找到了就直接返回 } // 如果当前的slot已经无效,并且向前扫描过程中没有无效slot, // 则更新slotToExpunge为当前位置 if (k == null && slotToExpunge == staleSlot) slotToExpunge = i; } // key没找到就原地创建一个新的 tab[staleSlot].value = null; tab[staleSlot] = new Entry(key, value); // 在探测过程中如果发现任何无效slot, // 则做一次清理(连续段清理+启发式清理) if (slotToExpunge != staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);} /** * 这个函数做了两件事情: * 1)清理当前无效slot(由staleSlot指定位置) * 2)从staleSlot开始,一直到null位,清理掉中间所有的无效slot */private int expungeStaleEntry(int staleSlot) { Entry[] tab = table; int len = tab.length; // 清理当前无效slot(由staleSlot指定位置) tab[staleSlot].value = null; tab[staleSlot] = null; size--; // 从staleSlot开始,一直到null位,清理掉中间所有的无效slot Entry e; int i; for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex { ThreadLocal k = e.get(); if (k == null) { // 清理掉无效slot e.value = null; tab[i] = null; size--; } else { int h = k.threadLocalHashCode & ; // 当前ThreadLocal不在它计算出来的哈希位上, // 说明之前在插入的时候被顺延到哈希位后面放置了, // 因此此时需要向前调整位置 if  { tab[i] = null; // 从计算出来的哈希位开始往后查找,找到一个适合它的空位 while (tab[h] != null) h = nextIndex; tab[h] = e; } } } return i;} /** * 启发式地清理slot, * n是用于控制控制扫描次数的 * 正常情况下如果log2次扫描没有发现无效slot,函数就结束了 * 但是如果发现了无效的slot,将n置为table的长度len,做一次连续段的清理 * 再从下一个空的slot开始继续扫描 */private boolean cleanSomeSlots(int i, int n) { boolean removed = false; Entry[] tab = table; int len = tab.length; do { i = nextIndex; Entry e = tab[i]; if (e != null && e.get() == null) { n = len; removed = true; i = expungeStaleEntry; } } while ( (n >>>= 1) != 0); return removed;}
    

    上面作者来图解一下expungeStaleEntry办法的流水生产线:

    图片 4expungeStaleEntry方法

    上述是ThreadLocal源码介绍的全体内容。上边小编将增加补充部分在实际付出进度中遇见的难点,作为补偿音讯一并分享。

    PrimerFinder.java:

    参考

    • What is the significance of load factor in HashMap?
    • The Java HashMap Under the Hood

    resources/application.yml

    downloader我们也叫做下载器,首要功用正是拜谒网络并打响抓回我们要的数码:举个例子html网页、json/xml数据、二进制流(图片、office文书档案等)近期NetDiscovery支持的downloader完毕有:

    图片 5

    运行结果:

    get() 方法

    为了获得存款和储蓄在hashMap中的对象,我们要求掌握与它对应的key。然后经过get方法把相应的key传到参数里。线程密封之ThreadLocal源码详解,知识点速记。调用HashMap的get方法的时候,也会调用key对象的hashCode方法

     @Test public void mapKeyTest(){ HashMap<MyKey,String> map = new HashMap<MyKey, String>(); MyKey key1 = new MyKey; map.put(key1,"value1"); String retV = map.get; }
    

    调整台上能够看出两行输出

    调用 hashCode()调用 hashCode()
    

    本文由澳门皇家赌场真人在线发布于皇家赌场游戏,转载请注明出处:线程密封之ThreadLocal源码详解,知识点速记

    关键词:

上一篇:没有了

下一篇:没有了