精华内容
下载资源
问答
  • 伟大的科学家爱因斯坦有过两次婚姻,他的第一位夫人Mileva是他的学姐,大他几岁; 我们可以看到新婚的这对爱侣都对未来憧憬无比,眼神里面的那种对爱和家庭的美好向往,流露出来每一个人在面对爱情和婚姻的情愫; ...

    伟大的科学家爱因斯坦有过两次婚姻,他的第一位夫人Mileva是他的学姐,大他几岁;


    我们可以看到新婚的这对爱侣都对未来憧憬无比,眼神里面的那种对爱和家庭的美好向往,流露出来每一个人在面对爱情和婚姻的情愫;而其实这位师姐也是一位天才级别的学霸,据说对爱因斯坦相对论的完成有不可缺的贡献,大概是早期理解爱因斯坦理论假说的第一人;爱因斯坦得到诺奖之后大部分奖金赠给了Mileva,遗憾的是得诺奖时他们已经离婚了;

    再贴一张这位女学霸的单人照

    在我对MBTI理论的学习参悟过程中,我发现对人做最快速判断 往往从其个人单人照片可以看出来;人对人的影响非常大,因为人类都有足够强大的下丘脑,掌握人类的情绪和激起程度,在和他人相处的过程里,特别是跟性格类型迥异的人相处,多多少少都会受到对方的影响,投射到自己的心绪、表情、特别是眼神,可以看到一些与平时不一样的状态,而这种状态对判断MBTI的人格类型是挖了坑的,所以最好寻找到人物足够自我的状态,展示其自身独有的自我,这样的情境下抓到的照片可以作为MBTI性格类型分类的重要参考;

    以上说了这么个特别的心得,也许有朋友不懂性格类型,我在这里先扫盲一下啊,有兴趣探索这一块的朋友可以谷歌或者百度一下personality celebrity这些相关的网站往往贴出来很多名人的MBTI划分,大家可以多多看一下相关的照片,比如看看ESTP类型的,里面的布拉德皮特、朱莉等等人物,其面相往往有一些普遍类似的特点,特别是眼神、情绪状态等等方面,我们可以藉此来加速对MBTI的一些认知;


    如果您有兴致,可以按照16种人格分类来依次的熟悉各种不同类型任务面部特征;由于人类强大的直觉和归纳总结能力,很快您也可以有不错的类型认知了;


    那么我们来看,爱因斯坦和这位 Mileva的婚姻,我们尝试给出一个理论假设:两位相同人格的INTP类型的人,不适合做伴侣;那么第一步需要判断图中的两位学霸天才是INTP类型;如何判断呢?除了用上述的类型照片的分类法,我们还可以用MBTI的功能理论来判断;INTP是introvert thinker with intuiation  , 也即内向思考型+外向直觉;这种类型的人首先由于性格的内向,能量所能触及的空间往往局限于狭窄的抽象领域,当两个INTP结婚之后,生活中的很多琐事是两个人都不擅长的,当两个人遇到困难,解决的思路往往局限在一种内向的、有限的、博弈型的空间内,那么容易产生不可调和的矛盾或者难以达到足够融合的亲密关系;我们可以肯定爱因斯坦也遇到了人生中的这个难题,特别是随着年龄的增大,年轻时的荷尔蒙减退,彼此强烈的激情和肉体联结逐渐式微,那么性格中不可弭平的障碍就成了巨大的排斥力,让两个极高智商的天才学霸走向陌路;


    似乎我们还没有解决假说的证明,以后等我用更好的方式来解释吧;


    爱因斯坦和堂妹ELSA的婚姻质量怎么样我们不太清楚,据说ELSA把爱因斯坦照顾的很好,生活起居都不错;那么她是什么类型呢?

    先上图


    这一张图片已经足够近了,但是这一张图片不足以给我们足够信息,能看到人物的眼神,但是这个眼神未必是她本我的状态;

    继续上图


    这也是足够近距离,但是我们发现跟第一张的神情完全不一样哦;

    ok,再来一张



    其实这位爱因斯坦夫人是ESTP,没错,就是跟布拉德皮特、朱莉类似的人格类型,您可以翻看ESTP名人照片对比一下。我们可以发现,当她和爱因斯坦在一起时,出来的状态是不一样的,非本我的状态;感兴趣的同学可以谷歌ELSA EINSTAIN  来探索这个女人的世界;我不太喜欢她;并非因为她不是学霸,而是我觉得这个女人牺牲的太多了,真的活的不够自我;她也是二婚,爱因斯坦也是二婚,他们俩是堂兄妹,都姓爱;我不知道那个年代那个国度对二婚女人怎么看,但是这个女人生命的一切荣耀和价值似乎都来源于物理学家的丈夫;她活的比较累。






    展开全文
  • 我觉得“节流”和“去抖”应该结合在一起:间隔时间段内,只执行一。 方案三:其实所有的接口请求都有这个需求,因此可以对ajax封装一个代理层 代理层可以做个事情 缓存接口返回数据,可以缓存到...

    解决方案:我这里暂时使用的方案一

    方案一:申明一个变量,点击时置灰提交按钮。等接口调用结束放开按钮,就是你在点击之后,ajax请求完成之前将那个按钮禁止点击

    <template>
    <div>
        <!-- 其他代码 -->
        <button v-if="canSave" @click="save">提交</button>
        <button v-else disabled>提交</button>
    </div>
    </template>
    <script>
    export default {
        data(){
            return {
                canSave: true,
            }
        },
        methods: {
            save(){
                if(!canSave){
                    return;
                }
                this.canSave = false;
                // AJAX 结束后 this.canSave = true;
            },
        }
    }
    </script>
    

    方案二:加防抖
    我觉得“节流”和“去抖”应该结合在一起:间隔时间段内,只执行一次。

    方案三:其实所有的接口请求都有这个需求,因此可以对ajax封装一个代理层

    代理层可以做两个事情

    缓存接口返回数据,可以缓存到sessionstorage,也可以缓存到内存,相同的请求先访问缓存
    给每个请求接口加状态,未请求、请求中、已完成。未请求时去请求接口,同时把此接口状态置为请求中;请求中时再请求接口,将回调保存起来,接口数据返回以后,将数据存储到缓存,一起回调;已完成时请求接口,直接从缓存中读取数据,不再请求接口
    这样,不光是vue项目,也不光是post或者提交表单,所有的接口请求都具备了你需要的能力。

    展开全文
  • 这里有一道经典的面试题:“一个线程两次调用start()方法会出现什么情况?谈谈线程的生命周期和状态转移。” 我们就以这道题目为切入点深入聊聊线程吧。 典型回答 Java的线程是不允许启动两次的,第二次调用必然会...

    今天我们来聊一聊线程,提起线程大家肯定都不陌生,它是J ava并发的基础元素,理解、操纵、诊断线程也是Java工程师的一门必修课,但是你真的掌握线程了吗?
    这里有一道经典的面试题:“一个线程两次调用start()方法会出现什么情况?谈谈线程的生命周期和状态转移。” 我们就以这道题目为切入点深入聊聊线程吧。
    典型回答
    Java的线程是不允许启动两次的,第二次调用必然会抛出IllegalThreadStateException,这是一种运行时异常,多次调用start被认为是编程错误。
    关于线程生命周期的不同状态,在Java 5以后,线程状态被明确定义在其公共内部枚举类型java.lang.Thread.State中,分别是:
    新建(NEW),表示线程被创建出来还没真正启动的状态,可以认为它是个Java内部状态。
    就绪(RUNNABLE),表示该线程已经在JVM中执行,当然由于执行需要计算资源,它可能是正在运行,也可能还在等待系统分配给它CPU片段,在就绪队列里面排队。
    • 在其他一些分析中,会额外区分一种状态RUNNING,但是从Java API的角度,并不能表示出来。
    阻塞(BLOCKED),这个状态和我们前面两讲介绍的同步非常相关,阻塞表示线程在等待Monitor lock。比如,线程试图通过synchronized去获取某个锁,但是其他线程已经独占了,那么当前线程就会处于阻塞状态。
    等待(WAITING),表示正在等待其他线程采取某些操作。一个常见的场景是类似生产者消费者模式,发现任务条件尚未满足,就让当前消费者线程等待(wait),另外的生产者线程去准备任务数据,然后通过类似notify等动作,通知消费线程可以继续工作了。Thread.join()也会令线程进入等待状态。
    计时等待(TIMED_WAIT),其进入条件和等待状态类似,但是调用的是存在超时条件的方法,比如wait或join等方法的指定超时版本,如下面示例:
    public final native void wait(long timeout) throws InterruptedException;
    终止(TERMINATED),不管是意外退出还是正常执行结束,线程已经完成使命,终止运行,也有人把这个状态叫作死亡。
    在第二次调用start()方法的时候,线程可能处于终止或者其他(非NEW)状态,但是不论如何,都是不可以再次启动的。

    考点分析
    今天的问题可以算是个常见的面试热身题目,前面的给出的典型回答,算是对基本状态和简单流转的一个介绍,如果觉得还不够直观,我在下面分析会对比一个状态图进行介绍。总的来说,理解线程对于我们日常开发或者诊断分析,都是不可或缺的基础。
    面试官可能会以此为契机,从各种不同角度考察你对线程的掌握:
    • 相对理论一些的面试官可以会问你线程到底是什么以及Java底层实现方式。
    • 线程状态的切换,以及和锁等并发工具类的互动。
    • 线程编程时容易踩的坑与建议等。
    可以看出,仅仅是一个线程,就有非常多的内容需要掌握。我们选择重点内容,开始进入详细分析。

    知识扩展
    首先,我们来整体看一下线程是什么?
    从操作系统的角度,可以简单认为,线程是系统调度的最小单元,一个进程可以包含多个线程,作为任务的真正运作者,有自己的栈(Stack)、寄存器(Register)、本地存储(Thread Local)等,但是会和进程内其他线程共享文件描述符、虚拟地址空间等。
    在具体实现中,线程还分为内核线程、用户线程,Java的线程实现其实是与虚拟机相关的。对于我们最熟悉的Sun/Oracle JDK,其线程也经历了一个演进过程,基本上在Java 1.2之后,JDK已经抛弃了所谓的Green Thread,也就是用户调度的线程,现在的模型是一对一映射到操作系统内核线程。
    如果我们来看Thread的源码,你会发现其基本操作逻辑大都是以JNI形式调用的本地代码。

    	private native void start0();
    	private native void setPriority0(int newPriority);
        private native void interrupt0();
    

    这种实现有利有弊,总体上来说,Java语言得益于精细粒度的线程和相关的并发操作,其构建高扩展性的大型应用的能力已经毋庸置疑。但是,其复杂性也提高了并发编程的门槛,近几年的Go语言等提供了协程(coroutine),大大提高了构建并发应用的效率。于此同时,Java也在Loom项目中,孕育新的类似轻量级用户线程(Fiber)等机制,也许在不久的将来就可以在新版JDK中使用到它。
    下面,我来分析下线程的基本操作。如何创建线程想必你已经非常熟悉了,请看下面的例子:

    Runnable task = () -> {System.out.println("Hello World!");};
    Thread myThread = new Thread(task);
    myThread.start();
    myThread.join();
    

    我们可以直接扩展Thread类,然后实例化。但在本例中,我选取了另外一种方式,就是实现一个Runnable,将代码逻放在Runnable中,然后构建Thread并启动(start),等待结束(join)。
    Runnable的好处是,不会受Java不支持类多继承的限制,重用代码实现,当我们需要重复执行相应逻辑时优点明显。而且,也能更好的与现代Java并发库中的Executor之类框架结合使用,比如将上面start和join的逻辑完全写成下面的结构:

    Future future = Executors.newFixedThreadPool(1)
    submit(task)
    get();
    

    这样我们就不用操心线程的创建和管理,也能利用Future等机制更好地处理执行结果。线程生命周期通常和业务之间没有本质联系,混淆实现需求和业务需求,就会降低开发的效率。
    从线程生命周期的状态开始展开,那么在Java编程中,有哪些因素可能影响线程的状态呢?主要有:
    • 线程自身的方法,除了start,还有多个join方法,等待线程结束;yield是告诉调度器,主动让出CPU;另外,就是一些已经被标记为过时的resume、stop、suspend之类,据我所知,在JDK最新版本中,destory/stop方法将被直接移除。
    • 基类Object提供了一些基础的wait/notify/notifyAll方法。如果我们持有某个对象的Monitor锁,调用wait会让当前线程处于等待状态,直到其他线程notify或者notifyAll。所以,本质上是提供了Monitor的获取和释放的能力,是基本的线程间通信方式。
    • 并发类库中的工具,比如CountDownLatch.await()会让当前线程进入等待状态,直到latch被基数为0,这可以看作是线程间通信的Signal。

    我这里画了一个状态和方法之间的对应图:
    在这里插入图片描述

    Thread和Object的方法,听起来简单,但是实际应用中被证明非常晦涩、易错,这也是为什么Java后来又引入了并发包。总的来说,有了并发包,大多数情况下,我们已经不再需要去调用wait/notify之类的方法了。
    前面谈了不少理论,下面谈谈线程API使用,我会侧重于平时工作学习中,容易被忽略的一些方面。
    先来看看守护线程(Daemon Thread),有的时候应用中需要一个长期驻留的服务程序,但是不希望其影响应用退出,就可以将其设置为守护线程,如果JVM发现只有守护线程存在时,将结束进程,具体可以参考下面代码段。注意,必须在线程启动之前设置。

    Thread daemonThread = new Thread();
    daemonThread.setDaemon(true);
    daemonThread.start();
    

    再来看看Spurious wakeup。尤其是在多核CPU的系统中,线程等待存在一种可能,就是在没有任何线程广播或者发出信号的情况下,线程就被唤醒,如果处理不当就可能出现诡异的并发问题,所以我们在等待条件过程中,建议采用下面模式来书写。

    	// 推荐
    	while ( isCondition()) {
    	    waitForAConfition(...);
    	}
    	 	 
    // 不推荐,可能引入bug
    	if ( isCondition()) {
        waitForAConfition(...);
    	}
    

    Thread.onSpinWait(),这是Java 9中引入的特性。我在专栏第16讲给你留的思考题中,提到“自旋锁”(spin-wait, busy-waiting),也可以认为其不算是一种锁,而是一种针对短期等待的性能优化技术。“onSpinWait()”没有任何行为上的保证,而是对JVM的一个暗示,JVM可能会利用CPU的pause指令进一步提高性能,性能特别敏感的应用可以关注。
    再有就是慎用ThreadLocal,这是Java提供的一种保存线程私有信息的机制,因为其在整个线程声明周期内有效,所以可以方便地在一个线程关联的不同业务模块之间传递信息,比如事务ID、Cookie等上下文相关信息。
    它的实现结构,可以参考源码,数据存储于线程相关的ThreadLocalMap,其内部条目是弱引用,如下面片段。

    static class ThreadLocalMap {
    
    	    static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
           Object value;
           Entry(ThreadLocal<?> k, Object v) {
                super(k);
              value = v;
    	        }
        }
       // …
    }
    

    当Key为null时,该条目就变成“废弃条目”,相关“value”的回收,往往依赖于几个关键点,即set、remove、rehash。
    下面是set的示例,我进行了精简和注释:

    	private void set(ThreadLocal<?> key, Object value) {
    	    Entry[] tab = table;
    	    int len = tab.length;
    	    int i = key.threadLocalHashCode & (len-1);
    	 
    	    for (Entry e = tab[i];; …) {
    	        //…
            if (k == null) {
    	        // 替换废弃条目
    	            replaceStaleEntry(key, value, i);
    	            return;
    	        }
    	    }
    	 
    	    tab[i] = new Entry(key, value);
    	    int sz = ++size;
    	    //  扫描并清理发现的废弃条目,并检查容量是否超限
    	    if (!cleanSomeSlots(i, sz) && sz >= threshold)
    	        rehash();// 清理废弃条目,如果仍然超限,则扩容(加倍)
    	}
    

    具体的清理逻辑是实现在cleanSomeSlots和expungeStaleEntry之中,如果你有兴趣可以自行阅读。

    今天,我们介绍了线程基础,分析了生命周期中的状态和各种方法之间的对应关系,这也有助于我们更好地理解synchronized和锁的影响,并介绍了一些需要注意的操作,希望对你有所帮助。

    展开全文
  • 大家好,我是 Oracle首席工程师...今天我要问你的问题是,一个线程两次调用start()方法会出现什么情况?谈谈线程的生命周期和状态转移。   典型回答 Java的线程是不允许启动两次的,第二次调用必然会抛出Illeg...

    大家好,我是 Oracle首席工程师杨晓峰。 今天想和大家深入聊聊线程,相信大家对于线程这个概念都不陌生,它是Java并发的基础元素,理解、操纵、诊断线程是Java工程师的必修课,但是你真的掌握线程了吗?

    今天我要问你的问题是,一个线程两次调用start()方法会出现什么情况?谈谈线程的生命周期和状态转移。

     

    典型回答

    Java的线程是不允许启动两次的,第二次调用必然会抛出IllegalThreadStateException,这是一种运行时异常,多次调用start被认为是编程错误。

    关于线程生命周期的不同状态,在Java 5以后,线程状态被明确定义在其公共内部枚举类型java.lang.Thread.State中,分别是:

    • 新建(NEW),表示线程被创建出来还没真正启动的状态,可以认为它是个Java内部状态。
    • 就绪(RUNNABLE),表示该线程已经在JVM中执行,当然由于执行需要计算资源,它可能是正在运行,也可能还在等待系统分配给它CPU片段,在就绪队列里面排队。
    • 在其他一些分析中,会额外区分一种状态RUNNING,但是从Java API的角度,并不能表示出来。
    • 阻塞(BLOCKED),这个状态和我们前面两讲介绍的同步非常相关,阻塞表示线程在等待Monitor lock。比如,线程试图通过synchronized去获取某个锁,但是其他线程已经独占了,那么当前线程就会处于阻塞状态。
    • 等待(WAITING),表示正在等待其他线程采取某些操作。一个常见的场景是类似生产者消费者模式,发现任务条件尚未满足,就让当前消费者线程等待(wait),另外的生产者线程去准备任务数据,然后通过类似notify等动作,通知消费线程可以继续工作了。Thread.join()也会令线程进入等待状态。
    • 计时等待(TIMED_WAIT),其进入条件和等待状态类似,但是调用的是存在超时条件的方法,比如wait或join等方法的指定超时版本,如下面示例:

    public final native void wait(long timeout) throws InterruptedException;

    • 终止(TERMINATED),不管是意外退出还是正常执行结束,线程已经完成使命,终止运行,也有人把这个状态叫作死亡。

    在第二次调用start()方法的时候,线程可能处于终止或者其他(非NEW)状态,但是不论如何,都是不可以再次启动的。

     

    考点分析

    今天的问题可以算是个常见的面试热身题目,前面的给出的典型回答,算是对基本状态和简单流转的一个介绍,如果觉得还不够直观,我在下面分析会对比一个状态图进行介绍。总的来说,理解线程对于我们日常开发或者诊断分析,都是不可或缺的基础。

    面试官可能会以此为契机,从各种不同角度考察你对线程的掌握:

    • 相对理论一些的面试官可以会问你线程到底是什么以及Java底层实现方式。
    • 线程状态的切换,以及和锁等并发工具类的互动。
    • 线程编程时容易踩的坑与建议等。

    可以看出,仅仅是一个线程,就有非常多的内容需要掌握。我们选择重点内容,开始进入详细分析。

     

    知识扩展

    首先,我们来整体看一下线程是什么?

    从操作系统的角度,可以简单认为,线程是系统调度的最小单元,一个进程可以包含多个线程,作为任务的真正运作者,有自己的栈(Stack)、寄存器(Register)、本地存储(Thread Local)等,但是会和进程内其他线程共享文件描述符、虚拟地址空间等。

    在具体实现中,线程还分为内核线程、用户线程,Java的线程实现其实是与虚拟机相关的。对于我们最熟悉的Sun/Oracle JDK,其线程也经历了一个演进过程,基本上在Java 1.2之后,JDK已经抛弃了所谓的Green Thread,也就是用户调度的线程,现在的模型是一对一映射到操作系统内核线程。

    如果我们来看Thread的源码,你会发现其基本操作逻辑大都是以JNI形式调用的本地代码。

    private native void start0();
    private native void setPriority0(int newPriority);
    private native void interrupt0();

    这种实现有利有弊,总体上来说,Java语言得益于精细粒度的线程和相关的并发操作,其构建高扩展性的大型应用的能力已经毋庸置疑。但是,其复杂性也提高了并发编程的门槛,近几年的Go语言等提供了协程(coroutine),大大提高了构建并发应用的效率。于此同时,Java也在Loom项目中,孕育新的类似轻量级用户线程(Fiber)等机制,也许在不久的将来就可以在新版JDK中使用到它。

    下面,我来分析下线程的基本操作。如何创建线程想必你已经非常熟悉了,请看下面的例子:

    Runnable task = () -> {System.out.println("Hello World!");};
    Thread myThread = new Thread(task);
    myThread.start();
    myThread.join();

    我们可以直接扩展Thread类,然后实例化。但在本例中,我选取了另外一种方式,就是实现一个Runnable,将代码逻放在Runnable中,然后构建Thread并启动(start),等待结束(join)。

    Runnable的好处是,不会受Java不支持类多继承的限制,重用代码实现,当我们需要重复执行相应逻辑时优点明显。而且,也能更好的与现代Java并发库中的Executor之类框架结合使用,比如将上面start和join的逻辑完全写成下面的结构:

    Future future = Executors.newFixedThreadPool(1)
    .submit(task)
    .get();

    这样我们就不用操心线程的创建和管理,也能利用Future等机制更好地处理执行结果。线程生命周期通常和业务之间没有本质联系,混淆实现需求和业务需求,就会降低开发的效率。

    从线程生命周期的状态开始展开,那么在Java编程中,有哪些因素可能影响线程的状态呢?主要有:

    • 线程自身的方法,除了start,还有多个join方法,等待线程结束;yield是告诉调度器,主动让出CPU;另外,就是一些已经被标记为过时的resume、stop、suspend之类,据我所知,在JDK最新版本中,destory/stop方法将被直接移除。
    • 基类Object提供了一些基础的wait/notify/notifyAll方法。如果我们持有某个对象的Monitor锁,调用wait会让当前线程处于等待状态,直到其他线程notify或者notifyAll。所以,本质上是提供了Monitor的获取和释放的能力,是基本的线程间通信方式。
    • 并发类库中的工具,比如CountDownLatch.await()会让当前线程进入等待状态,直到latch被基数为0,这可以看作是线程间通信的Signal。

     

    我这里画了一个状态和方法之间的对应图:

    Thread和Object的方法,听起来简单,但是实际应用中被证明非常晦涩、易错,这也是为什么Java后来又引入了并发包。总的来说,有了并发包,大多数情况下,我们已经不再需要去调用wait/notify之类的方法了。

    前面谈了不少理论,下面谈谈线程API使用,我会侧重于平时工作学习中,容易被忽略的一些方面。

    先来看看守护线程(Daemon Thread),有的时候应用中需要一个长期驻留的服务程序,但是不希望其影响应用退出,就可以将其设置为守护线程,如果JVM发现只有守护线程存在时,将结束进程,具体可以参考下面代码段。注意,必须在线程启动之前设置。

    Thread daemonThread = new Thread();
    daemonThread.setDaemon(true);
    daemonThread.start();

    再来看看Spurious wakeup。尤其是在多核CPU的系统中,线程等待存在一种可能,就是在没有任何线程广播或者发出信号的情况下,线程就被唤醒,如果处理不当就可能出现诡异的并发问题,所以我们在等待条件过程中,建议采用下面模式来书写。

    // 推荐
    while ( isCondition()) {
        waitForAConfition(...);
    }
    
    
    // 不推荐,可能引入bug
    if ( isCondition()) {
        waitForAConfition(...);
    }

    Thread.onSpinWait(),这是Java 9中引入的特性。我在专栏第16讲给你留的思考题中,提到“自旋锁”(spin-wait, busy-waiting),也可以认为其不算是一种锁,而是一种针对短期等待的性能优化技术。“onSpinWait()”没有任何行为上的保证,而是对JVM的一个暗示,JVM可能会利用CPU的pause指令进一步提高性能,性能特别敏感的应用可以关注。

    再有就是慎用ThreadLocal,这是Java提供的一种保存线程私有信息的机制,因为其在整个线程声明周期内有效,所以可以方便地在一个线程关联的不同业务模块之间传递信息,比如事务ID、Cookie等上下文相关信息。

    它的实现结构,可以参考源码,数据存储于线程相关的ThreadLocalMap,其内部条目是弱引用,如下面片段。

    static class ThreadLocalMap {
    
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
       // …
    }

    当Key为null时,该条目就变成“废弃条目”,相关“value”的回收,往往依赖于几个关键点,即set、remove、rehash。

    下面是set的示例,我进行了精简和注释:

    private void set(ThreadLocal<?> key, Object value) {
        Entry[] tab = table;
        int len = tab.length;
        int i = key.threadLocalHashCode & (len-1);
    
        for (Entry e = tab[i];; …) {
            //…
            if (k == null) {
            // 替换废弃条目
                replaceStaleEntry(key, value, i);
                return;
            }
        }
    
        tab[i] = new Entry(key, value);
        int sz = ++size;
        //  扫描并清理发现的废弃条目,并检查容量是否超限
        if (!cleanSomeSlots(i, sz) && sz >= threshold)
            rehash();// 清理废弃条目,如果仍然超限,则扩容(加倍)
    }

    具体的清理逻辑是实现在cleanSomeSlots和expungeStaleEntry之中,如果你有兴趣可以自行阅读。

     

    结合专栏第4讲的介绍引用类型,我们会发现一个特别的地方,通常弱引用都会和引用队列配合清理机制使用,但是ThreadLocal是个例外,它并没有这么做。

    这意味着,废弃项目的回收依赖于显式地触发,否则就要等待线程结束,进而回收相应ThreadLocalMap!这就是很多OOM的来源,所以通常都会建议,应用一定要自己负责remove,并且不要和线程池配合,因为worker线程往往是不会退出的。

     

    今天,我介绍了线程基础,分析了生命周期中的状态和各种方法之间的对应关系,这也有助于我们更好地理解synchronized和锁的影响,并介绍了一些需要注意的操作,希望对你有所帮助。

    展开全文
  • UDP分两次接收数据包 MSG_PEEK

    万次阅读 2020-09-22 17:51:33
    今天在测试e_link协议时,使用SOCK_DGRAM UDP连接服务器,由于协议由定长头部,加数据组成,且头部中包含了数据的长度,因此设计时分两次进行接收。 先接收头部,解析出数据长度,再次接收数据。 但头部接收正常,...
  • 首先想到是看看什么占用了18191端口: [root@centos02 app]# netstat -tunlp|grep 18191 tcp 0 0 :::18191 :::* LISTEN 23049/jsvc.exec [root@centos02 app]# ps -ef|grep 23049 tomcat 23049 ...
  • 寻找数组中唯一出现两次的数

    千次阅读 2018-04-14 16:40:05
    如果允许使用额外的空间,那就比较好吧,这些数,只有一个出现了两次,其他都只出现一次,那么,我们就用个数组来统计一下每个数出现的次数,在统计过程中,如果出现一个是出现2次的话,那么毫无疑问,程序可以结束...
  • android baseadapter的getview调用两次

    千次阅读 2012-07-01 20:37:47
    android baseadapter的getview调用两次   今天搞了4个小时。 main,xml 里面放的listview     android:layout_width="match_parent"  android:layout_height="wrap_content" listview的宽高这样设置! ...
  • 表单实例(判断两次密码是否一致) 一、实例描述: 本例是一个综合性的练习,除了们正在学习的JS知识外,还用到了HTML的表格,表单等相关知识。   二、截图     三、代码 1 &lt;!DOCTYPE html&...
  • 本文将会从一个爱情的故事开始,着力从什么是握手、为什么是而不是俩或者四、TCP关闭的过程、为什么要四挥手、而不是三、为什么最后是2MSL、什么是2MSL这些问题展开分析,详解,让你一文弄懂三...
  • 场景:react+antd+redux项目,在使用表单时,无表单验证的表单元素change只触发一次,而有表单验证的表单元素会触发两次(有些是多次) 问题原因(待商榷): 以下为截取的部分解释: 应该是getFieldProps 出的问题...
  • 这篇文章说下他们结合使用的种连接方式。(AWL与直连) 先看一张图: 这是kafka与streaming结合的基本方式,如图spark集群中的 worker节点中 exeutor线程里的 receiver接口会一直消费kafka中的数据,那么问题来了...
  • 两次面试经历-360与百度

    千次阅读 2016-04-20 10:25:58
     第一面试是去360,准备了有2、3天,因为从毕业到现在是第一去别的公司面试,十分认真对待,也仿佛看到了实现自己在学校就想去大型互联网公司愿望的希望。约了10点面试,HR把我带上楼,让我坐下等了一会,周围...
  • 两次 group by 分组的sq语句与用法

    千次阅读 2018-07-18 14:55:37
     group by 一般和聚合函数一起使用才有意义,比如 count sum avg等,使用group by的个要素:  (1) 出现在select后面的字段 要么是是聚合函数中的,要么就是group by 中的.  (2) 要筛选结果 可以先使用where 再用...
  • 为什么不能用两次握手进行连接?”,想想最近也到金三银四了,所以就查阅了相关资料,整理出来了这篇文章,希望对你们有所帮助。 TCP 连接 我们先来补一下基础什么是 TCP 协议?传输控制协议( Transmission Control...
  • OpenWrt与Arduino的USB直接通信 By 船长@第一的硬软件结合 两者联系在一起,是我一直想做的一件事,因为两者都很小巧美妙。两者的连线,有种方式,ttl-ttl,还有usb-》usb。我喜欢第二种。 用串口...
  • 因为video控件标签又支持... 两者的结合使得手机移动端的网页播放得以完美的实现。 从而实现了所有观看直播和点播视频的大众无需再下载任何插件就可以欣赏视频内容。 从技术角度上讲,是一种视频观看欣赏体验的大飞跃。
  • 首先简单的介绍一下ES6是什么,可能很多人还是第一听说,我们都知道H5是html的新一代的标准,同样,ES6是javascript的新一代标准,全称是ECMAScript 6.0,简称ES6,其实不是什么神秘的东西。15年6月发布的。 今天...
  • 但是日志中start()出现了两次,并且this的地址不同,很明显,RefreshJob实例化了两次,造成两个Job实例在同时运行。 求Google,结合两篇文章的答案,分析得出: web.xml中的DispatcherServlet继承自...
  • 摘要: 《Linux多线程服务端编程:使用muduo C++网络库》这本书自今年一月上市以来,半年之内已经重印两次(加上首印,一共是三次印刷),总印数达到了9000册,这在技术书里已经算是相当不错的成绩。 以下谈一谈这...
  • 有个朋友看点击打开链接 里的三个线程“A线程打印10A,B线程打印10B,C线程打印10C,要求线程同时运行,交替打印10ABC。” 这个问题的代码,有点不太理解,让我给看下。不理解的原因也很简单,就是对wait和...
  • Android RxJava/RxAndroid结合Retrofit使用

    万次阅读 2016-04-07 19:52:30
    假设现在有个对象A和B,在A发生某种变化时要主动通知B。这就是观察者模式。Android里 View.setOnClickListener(new OnClickListener) 就是运用观察者模式的典型例子。在这个例子中View充当对象A的角色,...
  • Docker Hub结合Github自动化构建镜像

    千次阅读 2018-04-07 11:10:57
    Create Automated Build第一进行该步骤需要Github的授权。会自动跳到Github同意授权即可。在Github上创建一个Docker构建项目,在里面实现Dockerfile。该步骤可以在本地完成,然后提交到自己的Github仓库中。然后...
  • Unity与HTC开发结合

    千次阅读 2017-08-22 10:06:18
    VR效果真心不错,于是想着在UE4以及Unity中结合使用,UE4的结合颇为强大,步骤简单,Unity的结合出现了一些小问题,在他的帮助吓顺利解决,在这里做一些小总结,望各位学习者共同探讨学习,也在这里再一感谢这位...
  • 基础介绍: 给定给定区间,函数连续且,那么根据介值定理,函数必然在区间内有根...即给定个点,。其割线方程为,那么令,x的值即为下一迭代的结果。 逆二插值法:为割线法的进化版本。使用三个点确定一个二...
  • 第二,与方向传感器结合,通过旋转手机进行道路的方向确认。有了这个功能,地图已经可以为我服务了~~~~ 效果图: 好了,可以代码,为了方便,我把所有的按钮都放到了menu菜单中。 1、初次启动定位 /** * 定位...
  • 1. FPN解决了什么问题? 答: 在以往的faster rcnn进行目标检测时,无论是rpn还是fast rcnn,roi 都作用在最后一层,这在大目标的检测没有问题,但是对于小目标的检测就有些问题。因为对于小目标来说,当进行卷积...
  • C语言之优先级、结合性与自增运算

    千次阅读 2016-08-18 21:08:53
    优先级、结合性这些概念在初学的时候并没有放在心上,今天又碰到这个问题,查了不少资料,再次做个总结。   在标准C语言的文档里,对操作符的结合性并没有做出非常清楚的解释。一个满分的回答是:它是仲裁者,在...
  • Android:RxJava 结合 Retrofit 优雅实现 网络请求轮询

    万次阅读 多人点赞 2017-11-17 10:59:17
    // Retrofit把网络请求的URL分成了部分:一部分放在Retrofit对象里,另一部分放在网络请求接口里 // 如果接口里的url是一个完整的网址,那么放在Retrofit对象里的URL可以忽略 // 采用Observable接口 // get...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 841,920
精华内容 336,768
关键字:

两次结合是什么