精华内容
下载资源
问答
  • 我不太懂它这个死时间什么意思,看了半天还是理解不了为什么分散刷新没有死时间
  • 线程的状态应该是5个 创建、就绪、运行、堵塞、死亡 死亡是指线程的结束吗?什么情况下死亡
  • HashMap为什么会出现循环

    千次阅读 多人点赞 2019-08-01 14:56:22
    如果是在单线程下使用HashMap,自然是没有问题的,如果后期由于代码优化,这段逻辑引入了多线程并发执行,在一个未知的时间点,发现CPU占用100%,居高不下,通过查看堆栈,你惊讶的发现,线程都Hang在hashMap的...

    问题

    如果是在单线程下使用HashMap,自然是没有问题的,如果后期由于代码优化,这段逻辑引入了多线程并发执行,在一个未知的时间点,会发现CPU占用100%,居高不下,通过查看堆栈,你会惊讶的发现,线程都Hang在hashMap的get()方法上,服务重启之后,问题消失,过段时间可能又复现了。

    这是为什么?

    原因分析

    在了解来龙去脉之前,我们先看看HashMap的数据结构。

    在内部,HashMap使用一个Entry数组保存key、value数据,当一对key、value被加入时,会通过一个hash算法得到数组的下标index,算法很简单,根据key的hash值,对数组的大小取模 hash & (length-1),并把结果插入数组该位置,如果该位置上已经有元素了,就说明存在hash冲突,这样会在index位置生成链表。

    如果存在hash冲突,最惨的情况,就是所有元素都定位到同一个位置,形成一个长长的链表,这样get一个值时,最坏情况需要遍历所有节点,性能变成了O(n),所以元素的hash值算法和HashMap的初始化大小很重要。

    当插入一个新的节点时,如果不存在相同的key,则会判断当前内部元素是否已经达到阈值(默认是数组大小的0.75),如果已经达到阈值,会对数组进行扩容,也会对链表中的元素进行rehash。

    实现

    HashMap的put方法实现:

    1、判断key是否已经存在

    public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        // 如果key已经存在,则替换value,并返回旧值
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
    
        modCount++;
        // key不存在,则插入新的元素
        addEntry(hash, key, value, i);
        return null;
    }
    

    2、检查容量是否达到阈值threshold

    void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
    
        createEntry(hash, key, value, bucketIndex);
    }
    

    如果元素个数已经达到阈值,则扩容,并把原来的元素移动过去。

    3、扩容实现

    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        ...
    
        Entry[] newTable = new Entry[newCapacity];
        ...
        transfer(newTable, rehash);
        table = newTable;
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }
    

    这里会新建一个更大的数组,并通过transfer方法,移动元素。

    void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }
    

    移动的逻辑也很清晰,遍历原来table中每个位置的链表,并对每个元素进行重新hash,在新的newTable找到归宿,并插入。

    案例分析

    假设HashMap初始化大小为4,插入个3节点,不巧的是,这3个节点都hash到同一个位置,如果按照默认的负载因子的话,插入第3个节点就会扩容,为了验证效果,假设负载因子是1.

    void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }
    

    以上是节点移动的相关逻辑。

    插入第4个节点时,发生rehash,假设现在有两个线程同时进行,线程1和线程2,两个线程都会新建新的数组。

    假设 线程2 在执行到Entry<K,V> next = e.next;之后,cpu时间片用完了,这时变量e指向节点a,变量next指向节点b。

    线程1继续执行,很不巧,a、b、c节点rehash之后又是在同一个位置7,开始移动节点

    第一步,移动节点a

    第二步,移动节点b

    注意,这里的顺序是反过来的,继续移动节点c

    这个时候 线程1 的时间片用完,内部的table还没有设置成新的newTable, 线程2 开始执行,这时内部的引用关系如下:

    这时,在 线程2 中,变量e指向节点a,变量next指向节点b,开始执行循环体的剩余逻辑。

    Entry<K,V> next = e.next;
    int i = indexFor(e.hash, newCapacity);
    e.next = newTable[i];
    newTable[i] = e;
    e = next;
    

    执行之后的引用关系如下图

    执行后,变量e指向节点b,因为e不是null,则继续执行循环体,执行后的引用关系

    变量e又重新指回节点a,只能继续执行循环体,这里仔细分析下:
    1、执行完Entry<K,V> next = e.next;,目前节点a没有next,所以变量next指向null;
    2、e.next = newTable[i]; 其中 newTable[i] 指向节点b,那就是把a的next指向了节点b,这样a和b就相互引用了,形成了一个环;
    3、newTable[i] = e 把节点a放到了数组i位置;
    4、e = next; 把变量e赋值为null,因为第一步中变量next就是指向null;

    所以最终的引用关系是这样的:

    节点a和b互相引用,形成了一个环,当在数组该位置get寻找对应的key时,就发生了死循环。

    另外,如果线程2把newTable设置成到内部的table,节点c的数据就丢了,看来还有数据遗失的问题。

    总结

    所以在并发的情况,发生扩容时,可能会产生循环链表,在执行get的时候,会触发死循环,引起CPU的100%问题,所以一定要避免在并发环境下使用HashMap,并发环境下要使用ConcurrentHashmap。

    展开全文
  • 什么会出现循环链表呢? 产生循环链表的过程: 如下所示的hashmap, 有两个元素, 它们的key分别是1和3, 假设再增加一个元素时会触发扩容操作 此时线程1和线程2都执行put()操作, 便都会触发hashmap的扩容操作, 假...

    welcome to my blog

    问题描述: 并发情况下, hashmap在扩容时使用头插法可能出现循环链表, 后果就是调用get()方法时可能陷入死循环. 为什么会出现循环链表呢?

    产生循环链表的过程:

    如下所示的hashmap, 有两个元素, 它们的key分别是1和3, 假设再增加一个元素时会触发扩容操作
    在这里插入图片描述

    此时线程1和线程2都执行put()操作, 便都会触发hashmap的扩容操作,

    假设线程1扩容时, 执行完transfer()中的Entry<K,V> next = e.next;被挂起, 此时e指向1, next指向3, 如下图所示
    在这里插入图片描述
    假设1和3在新的数组中仍然发生哈希碰撞, 假设线程2完成了扩容, 那么此时哈希表的样子如下图所示
    可以发现, 由于使用了头插法, 所以3变成了头结点
    在这里插入图片描述
    回到线程1, e指向的是1, next指向的是3, 继续向下执行
    当执行完e.next = newTable[i];便出现了循环链表, 其中,newTable[i]是3
    在这里插入图片描述

    产生循环链表后带来的问题是什么?

    环形链表已经产生了, 当我们调用get(3)或者get(1)不会产生问题,
    但是如果get(5), 并且5在数组中的下标和1,3的一致的话, 由于链表中没有5, 所以就会一直在链表中寻找, 但是链表没有尽头, 就导致程序卡在get(5)处了

    展开全文
  • 什么Relu导致死亡节点

    千次阅读 2017-12-14 10:41:36
    如果learn_rate设置过大,在遇到负例的时候,W突然变的很小,导致所有样本在某一结点处,全部输出为负数, 由图可知,当y_为负数时候,梯度为0,则在此处权重将不会得到更新,这样便导致了死亡节点 或许...

    y代表真实值,y_代表预测值,损失函数采用交叉熵损失函数如下

    loss function:L(y,y_)=-(ylny_+(1-y)ln(1-y_))

    一般更新参数的方式,我们梯度下降的方式,目的是使得损失函数最小,达到一个能够接受的局部最小值,当然如果能到达全局最小最好。

    对损失函数的研究:

    当y=1,y_=1 L=-ln1=0
    当y=1,y_=0 L=无穷大
    当y=0,y_=1 L=无穷大
    当y=0,y_=0 L=0
    由损失函数可知,想要使得函数变小,也就是对于正例

    y=WTX+b

    y_要变大,则更新权重W,使得W变大,负例y_要变小,更新权重W使得W变小
    如果learn_rate设置过大,在遇到负例的时候,W会突然变的很小,会导致所有样本在某一结点处,全部输出为负数,

    这里写图片描述

    由图可知,当y_为负数时候,梯度为0,则在此处权重将不会得到更新,这样便导致了死亡节点
    或许你会发现使用L2正则化,或者其他的梯度下降方式如动能,RMS ,Adam等 可以避免,笔者认为他们可以改变的原理还是因为这些算法使得w在一次更新中变化的缓和了,所以想要尽量避免死亡节点,最好的办法还是学习率不要设置太大。
    在设置激活函数的时候,也可以试试其他的如上图 的右边两个,由图可知在抑制区的导数不为0,这也是一种避免死亡节点的手段。

    展开全文
  • 今天研读Java并发容器和框架时,看到为什么要使用ConcurrentHashMap时,其中有一个原因是:线程不安全的HashMap, HashMap在并发执行put操作时引起循环,是因为多线程导致HashMap的Entry链表形成环形数据结构,...

    今天研读Java并发容器和框架时,看到为什么要使用ConcurrentHashMap时,其中有一个原因是:线程不安全的HashMap, HashMap在并发执行put操作时会引起死循环,是因为多线程会导致HashMap的Entry链表形成环形数据结构,查找时会陷入死循环。纠起原因看了其他的博客,都比较抽象,所以这里以图形的方式展示一下,希望支持!


    (1)当往HashMap中添加元素时,会引起HashMap容器的扩容,原理不再解释,直接附源代码,如下:

    1. /**
    2. *
    3. * 往表中添加元素,如果插入元素之后,表长度不够,便会调用resize方法扩容
    4. */
    5. void addEntry(int hash, K key, V value, int bucketIndex) {
    6. Entry<K,V> e = table[bucketIndex];
    7. table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
    8. if (size++ >= threshold)
    9. resize( 2 * table.length);
    10. }
    11. /**
    12. * resize()方法如下,重要的是transfer方法,把旧表中的元素添加到新表中
    13. */
    14. void resize(int newCapacity) {
    15. Entry[] oldTable = table;
    16. int oldCapacity = oldTable.length;
    17. if (oldCapacity == MAXIMUM_CAPACITY) {
    18. threshold = Integer.MAX_VALUE;
    19. return;
    20. }
    21. Entry[] newTable = new Entry[newCapacity];
    22. transfer(newTable);
    23. table = newTable;
    24. threshold = ( int)(newCapacity * loadFactor);
    25. }
    (2)参考上面的代码,便引入到了transfer方法,(引入重点)这就是HashMap并发时,会引起死循环的根本原因所在,下面结合transfer的源代码,说明一下产生死循环的原理,先列transfer代码(这是里JDK7的源偌),如下:
    1. /**
    2. * Transfers all entries from current table to newTable.
    3. */
    4. void transfer(Entry[] newTable, boolean rehash) {
    5. int newCapacity = newTable.length;
    6. for (Entry<K,V> e : table) {
    7. while( null != e) {
    8. Entry<K,V> next = e.next; ---------------------( 1)
    9. if (rehash) {
    10. e.hash = null == e.key ? 0 : hash(e.key);
    11. }
    12. int i = indexFor(e.hash, newCapacity);
    13. e.next = newTable[i];
    14. newTable[i] = e;
    15. e = next;
    16. } // while
    17. }
    18. }

    (3)假设:

    1. Map<Integer> map = new HashMap<Integer>( 2); // 只能放置两个元素,其中的threshold为1(表中只填充一个元素时),即插入元素为1时就扩容(由addEntry方法中得知)
    2. //放置2个元素 3 和 7,若要再放置元素8(经hash映射后不等于1)时,会引起扩容

    假设放置结果图如下:

         

     现在有两个线程A和B,都要执行put操作,即向表中添加元素,即线程A和线程B都会看到上面图的状态快照

    执行顺序如下:

                   执行一:  线程A执行到transfer函数中(1)处挂起(transfer函数代码中有标注)。此时在线程A的栈中

    1. e = 3
    2. next = 7

                  执行二:线程B执行 transfer函数中的while循环,即会把原来的table变成新一table(线程B自己的栈中),再写入到内存中。如下图(假设两个元素在新的hash函数下也会映射到同一个位置)


                 执行三: 线程A解挂,接着执行(看到的仍是旧表),即从transfer代码(1)处接着执行,当前的 e = 3, next = 7, 上面已经描述。

                              

                                 1. 处理元素 3 , 将 3 放入 线程A自己栈的新table中(新table是处于线程A自己栈中,是线程私有的,不肥线程2的影响),处理3后的图如下:

                                    2.  线程A再复制元素 7 ,当前 e = 7 ,而next值由于线程 B 修改了它的引用,所以next 为 3 ,处理后的新表如下图



                               3. 由于上面取到的next = 3, 接着while循环,即当前处理的结点为3, next就为null ,退出while循环,执行完while循环后,新表中的内容如下图:

     

                                  4. 当操作完成,执行查找时,会陷入死循环!

    转载自:https://blog.csdn.net/zhuqiuhui/article/details/51849692

    展开全文
  • apchche 假,每隔一段时间掉,只能手动重启
  • vc6.0调试的时候的补丁

    热门讨论 2011-08-05 11:13:34
    vc6.0经常debug的时候,或者调试的时候死掉,这个是专门解决vc6假的补丁,打上之后再也不担心调试一半的时候死掉了,嘿嘿
  • 原帖地址:... 进行put操作到阈值时,进行扩容的时候会导致循环 void transfer(Entry[] newTable) { Entry[] src = table; int newCapacity = newTable.length; //从OldTable将元素一个个拿出...
  • 一、什么叫金叉和叉 (一)什么叫金叉 金叉(黄金交叉)技术分析中的术语。指短期移动平均线向上穿过中期移动平均线或短期、金叉和叉中期移动平均线同时向上穿过长期移动平均线的走势图形。此交叉点是建仓的机会,...
  • 我的表是一对多的 也就是说 一张表里有另一个对象的成员变量 这样用那个工具org.codehaus.jackson.map.ObjectMapper...writeValueAsString(obj)的时候 出现循环 请问这该怎么解决(我是在SpringMVC框架下开发的)
  • JDK8中HashMap依然会死循环!

    千次阅读 2019-09-26 21:47:12
    JDK8中HashMap依然会死循环! ​ 是否你听说过JDK8之后HashMap以及解决的扩容循环的问题,虽然HashMap依然说线程不安全,但是不会造成服务器load飙升的问题。 ​ 然而事实并非如此。少年可曾了解一种红黑树成环的...
  • 这个时候,一些优化多年的老网站就遇到一个比较头疼的问题,由于代码批量替换,或者对应目录的文件夹发生了改变,就导致我们的网站一下多出了,很多的网站链接,网站长时间存在大量死链,通常出现,搜索...
  • 空的循环为什么会让cpu100%

    千次阅读 2018-07-19 17:10:04
     上面这个代码相信很多人都写过,这个代码的后果就是cpu100%(具体数值要看cpu核数和操作系统对这个的表示方法,有的2核显示50%,有的2核显示100%),如果是单核那么你的电脑相当卡了。解决这个问题的办法...
  • 只拿工资一个人的人生

    千次阅读 2018-06-08 10:07:48
    如果你工作几年,离开的时候,发现自己除了拿到几年固定的薪水,人脉也没有,资源也没有,个人能力也没提高多少,那么你的这段经历是失败的。你耗进去的是最美好的青春,你的青春就值这几万块钱吗?在现在这个社会,...
  • 什么死循环占用CPU高

    万次阅读 2011-08-08 21:57:48
    文章出处:...  为什么死循环占用CPU高 一个进程如果是循环,那么占有的CPU很高,可是操作系统时间片运行的,到了一定时间不是自动切换到别的进程吗?即便是循环,到时间还是
  • HashMap进行put操作引起循环?

    千次阅读 2020-07-03 13:22:33
    HashMap进行put操作引起循环? 最近在磕《java并发编程艺术》,在看到第六章的时候出现了下面这段我不是很理解的东西,如下 《java并发编程艺术》截取 为什么要使用ConcurrentHashMap 在并发编程中使用HashMap...
  • HashMap在jdk1.8中也会死循环

    万次阅读 2019-03-03 18:43:19
    jdk1.7版本中多线程同时对HashMap扩容时,引起链表循环,尽管jdk1.8修复了该问题,但是同样在jdk1.8版本中多线程操作hashMap时仍然引起循环,只是原因不一样。 示例代码 package com.gsonkeno.interview; ...
  • mysql杀的进程Sometimes MySQL queries take a very long time and slow the database ... 有时,MySQL查询花费很长时间,并减慢数据库处理速度。 我们可以找到并消除这些卡住的进程。 MySQL Kill过程步骤...
  • 实践证明,JDK8中HashMap依然会死循环

    万次阅读 2020-10-21 15:27:14
    是否你听说过JDK8之后HashMap已经解决的扩容循环的问题,虽然HashMap依然说线程不安全,但是不会造成服务器load飙升的问题。 然而事实并非如此。少年可曾了解一种红黑树成环的场景,=v= 今日在查看监控时候发现,...
  • 最近在维护一个外包遗留下的Hibernate+spring+spring mvc的一个项目,发现该项目运行一段时间后,发现APP请求后一个处于等待状态,直到请求超时,于是调试跟踪了一下,发现是操作数据库时一直没有返回,刚开始以为是...
  • 关键词:后台杀进程,OnDestroy执行吗? 【答案】:OnDestroy执行,也不会执行。 如果APP启动了MainActivity,MainActivity再启动了Activity1,Activity1再启动了Activity2。这时在后台手动杀进程(先显示...
  • 如图,hive提交查询的时候,在这步卡主不动假,也不报错,log也查不出来。 查了各种办法也没解决。 最后反思 1.不借助hive进行分析时候,仅仅是提交job跑mr没有问题 2.到hive上却假怀疑是Hive没有连接上...
  • 当oracle Session被锁时候

    千次阅读 2014-11-18 23:07:27
    无论是通过pl/sql Developer 还是通过sqlplus都无法更新,执行update语句或者执行select * from table_name for update 都一样,一直处于执行状态,无法完成,这时候我就猜想是不是session被锁了呢?带着这个问题,我们...
  • 保持在界面五六分钟左右就会卡,不能进行任何操作,点一下界面activity就finish了,不知道什么情况。 这个界面有定位监听,有后台server在运行,另外这个界面并没有内存泄露。 有没有大神遇见过这种问题?
  • 爬虫有时候会因为爬去某些网页速度极慢,影响性能。所有可以设置超时时间。 timeout单位秒 设置超时时间为0,使用try语句。#coding:utf-8 **urllib2** 超时可以通过 urllib2.urlopen() 的 timeout 参数直接设置。 ...
  • keil5打开项目有时候会卡

    千次阅读 2019-03-12 16:51:25
    去掉兼容模式试一下。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 985,931
精华内容 394,372
关键字:

如何的什么时候会死