当前位置:首页 > Java知识 > 正文内容

什么是超高频皮肤整形术(什么是超高频RFID)

abcsky883个月前 (01-26)Java知识55

本篇文章给大家谈谈

问题 1Java 在语法层面已经有了 synchronized 来实现管程,为什么还要在 JDK 中提供了 Lock 和 Condition 工具类来做这样的事情,这属于重复造轮子吗?答案首先你可能会想到的是 synchronized 性能问题,但是我想告诉你的是 synchronized 在高版本的 JDK 中性能已经得到了大幅的提升,很多开发者开始提倡使用 synchronized,性能问题可以不断优化提升,它并不是重载轮子的原因。

什么是超高频皮肤整形术(什么是超高频RFID)

大家都知道管程帮助我们解决了多线程资源共享问题,但同时也带来了死锁的风险Coffman 等人在 1971 年总结出来产生死锁的四个必要条件:1SEO搜索优化论文.互斥条件一个资源在同一时刻只能被一个线程操作2.占有且等待。

线程因为请求资源而阻塞时不会释放已经获取到的资源3.不可强行占有线程已经获取到的资源,在未释放前不允许被其他线程强行剥夺4.循环等待线程存在循环等待资源的关系(线程 T1 依次占有资源 A,B;线程 T2 依次占有资源 B,A;这就构成了循环等待资源关系)。

当发生死锁的时候必然上面四个条件都会满足,那么只要我们破坏其中的任何一个条件,我们即可解决死锁问题首先条件 1 无法破解,因为共享资源必须是互斥的,如果可以多个线程同时操作也没必要加锁了那我们试图使用 synchronized 来破解剩余的三个条件:。

1.占有且等待synchroSEO搜索优化论文nized 获取资源时候,只要资源获取不到,线程立即进入阻塞状态,并且不会释放已经获取的资源那么我们可以调整一下获取共享资源的方式,我们通过一个锁中介,通过中介一次性获取线程需要的所有资源,如果存在单个资源不满足情况,直接阻塞,而不是获取部分资源,这样我们即可解决这个问题,破解该条件。

2.不可强行占有synchronized 获取不到资源时候,线程直接阻塞,无法被中断释放资源,因此这个条件 synchronized 无法破解3.循环等待循环等待是因为线程在竞争 2 个互斥资源时候会以不同的顺序去获取资源,如果我们将线程获取资源的顺序固定下来即可破解这个条件。

综上我们可以知道 synchroniSEO搜索优化论文zed 不能破解“不可强行占有”条件,这就是 JDK 同时提供 Lock 这种管程的实现方式的原因当然啦,Lock 使用起来也更加的灵活例如我们有多个共享资源,锁是嵌套方式获取的,如线程需要先获取 A 锁,然后获取 B 锁,然后释放 A 锁,获取 C 锁,接着释放 B 锁,获取 D 锁 等等。

这种嵌套获取锁的方式 synchronized 是无法实现的,但是 Lock 却可以帮助我们来解决这个问题既然我们知道了 JDK 重造管程的原因,那我们来一起看一下 Lock 为我们提供的四种进入获取锁的方式:。

1.void lock();这种方式获取不到锁时候线程会进入阻塞状态,和 synchronizSEO搜索优化论文ed 类似2.void lockInterruptibly() throws InterruptedException;。

这种方式获取不到锁线程同样会进入阻塞,但是它可以接收中断信号,退出阻塞3.boolean tryLock();这种方式不管是否能获取到锁都不会阻塞而是立刻返回获取结果,成功返回 true,失败返回 false。

4.boolean tryLock(long time, TimeUnit unit) throws InterruptedException;这种方式获取不到锁的线程会进入阻塞状态,但是在指定的时间内如果仍未获得锁,则返回 false,否则返回 true。

问题 2你SEO搜索优化论文已经知道管程 synchronized 是通过锁对象所关联的 Monitor 对象的计数来标识线程对锁的占有状态,那么你知道 ReentrantLock 是如何来控制锁的占有状态吗?答案Lock 锁采用了与 Monitor 对象计数完全不同的一种方式,它依赖了并发包中的

AbstractQueuedSynchronizer (队列同步器简称 AQS ) 来实现在多线程情况下对锁状态的控制那么 AQS 是如何保证在多线程场景中锁状态可以正常控制呢,它主要使用了如下 3 个技术点:。

1.volatile 关键字首先 AQS 中定义了一个 volatile 修饰的 int 变量 state,大家都知道SEO搜索优化论文 volatile 是实现无锁编程的关键技术它有内存可见性和禁止指令重排序的功效,当多个线程去争夺锁的占有权的时候,如果有其中任何一个线程已经持有锁,它会设置 state 加 1。

这样由于 volatile 的可见性功效,其他线程会争取锁权限的时候发现锁已经被其他线程持有,就会进入等待队列当持有锁的线程使用结束后,释放锁,会将 state 减 1,然后去唤醒队列中等待的线程2.CAS(compare and swap) 算法

你是否存在疑问,当线程将 state 加 1 的时候是如何保证只有一个线程在做这个操作呢,毕竟在加 1 操作的时候线程尚未获得锁,所以加 1 操作存在竞争关系虽然 volaSEO搜索优化论文tile 可以解决线程间的内存可见性问题,但是仍然存在多个线程竞争 state 更新的问题,这就要使用我们的 CAS 算法来解决这个问题了,compare and swap 中文意思是比较并且替换。

它提供的功能是让我们提供一个预期值和一个即将要设置为的值,如果此时内存中的值正好是我们预期的值,则直接将值设置为我们即将要设置为的值拿竞争 state 举例,所有线程都希望 state 的值为 0 (0 代表没有线程占用),然后自己将 state 设置为 1(占有锁)。

在 AQS 中 由如下方法来帮我完成这件事情:protectedfinalbooleancompareAndSetState(intSEO搜索优化论文 expect, int update){ // See below for intrinsics setup to support this

return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } 你注意到了,该方法并不会直接帮我们完成这件事情而是一个 Unsafe 的对象完成了实际的操作,Unsafe 类提供了大量的原子方法,但是这些方法都是 native 标识的,代表它们是 JVM 提供的本地 C++ 方法,底层本质是对 CPU CAS 指令的一个封装。

这样 SEO搜索优化论文volatile 的内存可见性特效结合 CAS 算法就完美的解决了多线程并发获取锁的安全操作当然啦,Unsafe 正如其名,寓意为不安全,不推荐开发者直接使用,只是在 Java SDK 中使用到了。

3.双向队列到此你是否还存在疑问,多个线程竞争锁,一个线程获取到了锁,那其他线程怎么办呢?其他线程其实同样进入了阻塞等待状态(此处也使用了 Unsafe 的 park 本地方法),与 synchronized 不同是,AQS 内部提供了一个双向队列(不然怎么叫队列同步器呢,哈哈),队列的节点定义如下:

staticfinalclassNode {/**省略部分属性**/volatile Node prSEO搜索优化论文ev; volatile Node next; volatile Thread thread; Node nextWaiter;

/**省略部分方法**/ } 每一个进入等待队列的线程都会被关联到一个 Node,所有等待的线程按照先后顺序会组成一个双向队列(组成队列的过程中同样使用到了 CAS 算法 和 volatile 特性,不然多线程并发的给队列结尾添加节点也会存在竞争问题),等待持有锁的线程释放锁后唤醒队列头部的节点中关联的线程(此处唤醒同样使用了 Unsafe 的 unpark 本地方法),这样就有序的控制了SEO搜索优化论文线程对共享数据的并发访问。

如上我们介绍了 Lock 锁中用到的三个重要技术点和基本实现原理,相信问题回答到这一步面试官已经默默心里想:小伙子有点东西啊哈哈!问题 3HashMap 想必是平时工作中使用非常的频繁的数据结构了吧,它简单易用,同时也是面试扫盲第一轮技术面中出现频率极高的题目之一。

假设我问你:HashMap 一次 put 操作的详细过程是怎样的,你可以对答如流吗?我的答案HashMap 的存储结构就不用多介绍了,底层是一个 列表/二叉树 的数组结构当我们调用一次 put 操作的时候主要会经过如下 4 个大步骤,让我们来详细看一下这四个步骤都做了些什么事情。

1.求 Key 的 hashSEO搜索优化论文 值想要存入 HashMap 的元素的 key 都必须要经过一个 hash 运算,首先调用 key 对象的 hashCode 方法获取到对象的 hash 值然后将获得 hash 值向右位移 16 位,接着将位移后的值与原来的值做异或运算,即可获得到一个比较理想的 hash 值,之所以这样运算时为了尽可能降低 hash 碰撞(即使得元素均匀分布到各个槽中)。

2.确定 hash 槽获取到 hash 值后就是计算该元素所对应的 hash 槽,这一步操作比较简单直接将上一步操作中获取到的 hash 值与底层数组长度取模(为了提高性能,源码采用了运算,这样等价于取模运算,但是性能更好)获取 index SEO搜索优化论文位置。

3.将元素放入槽中在上一步中我们获得了 hash 槽的索引,接下来就是将元素放入到槽中,如果槽里当前没有任何元素则直接生成 Node 放入槽中,如果槽中已经有元素存在,则会有下面 2 种情况:3.1 槽里是一颗平衡二叉树

当列表的元素长度超过 8 时,为了加快槽上元素的索引速度,HashMap 会将列表转换为平衡二叉树所以在这种情况下,插入元素是在一颗平衡二叉树上做操作,查找和更新的时间复杂度都是 log(n),HashMap 会将元素包装为一个 TreeNode 添加到树上。

具体平衡二叉树的操作就不在此展开了,有兴趣的小伙伴可自行探索3.2 槽里是一个列表初始情况下,槽里面的元素是以列表SEO搜索优化论文形式存在的,HashMap 遍历列表将元素 更新 / 追加 到列表尾部元素添加后,HashMap 会判断当前列表元素个数,如达到 8 个元素则将列表转化为平衡二叉树,具体转换详情可参考 HashMap 中的方法 final void treeifyBin(Node[] tab, int hash) 。

4.扩容到这里时候,我们元素已经完美的放到了 HashMap 的存储中但是还有一个 HashMap 的自动扩容操作需要完成,默认情况下自动扩容因子是 0.75,即当容量为 16 时候,如果存储元素达到 16 * 0.75 = 12 个的时候,HashMap 会进行 resize 操作(俗称 rehSEO搜索优化论文ash)。

HashMap 会新建一个 2 倍容量与旧数组的数组,然后依次遍历旧的数组中的每个元素,将它们转移到新的数组当中其中有 2 个需要注意的点:列表中元素依然会保持原来的顺序和加入二叉树上的元素少达 6 个时候会将二叉树再次转化为列表来存储。

如上我们介绍了 HashMap 的依次 put 操作的全过程,小伙伴们快去看源码巩固一下吧,欢迎留言与我交流哦问题 4ConcurrentHashMap 在我们平时编码的过程中不是很常用,但是它在 Java 基础面试中是也是一道必考题,通常会拿出来和 HashMap 做比较,所以我们必须要掌握它。

那么,你知道 ConcurrentHashMap 是如SEO搜索优化论文何来实现一个支持并发读写的高效哈希表呢?我的答案ConcurrentHashMap 同 HashMap 一样,都是 Map 接口的子类不同之处在于 HashMap 是非线程安全的,如果想要在多线程环境下使用必须对它的操作添加互斥锁。

而 ConcurrentHashMap 为并发而生,可以在多线程环境下随便使用而不用开发人员去添加互斥锁那么 ConcurrentHashMap 是如何实现线程安全的特性呢,接下来我们就从它的 put 方法说开来,对比 HashMap 的 put 操作来一步步揭开它的神秘面纱。

1.获取哈希值 同 HashMap 一样,ConcurrentHashMap 底层也是使用SEO搜索优化论文一个 Node 的数组来存储数据因此第一步也是获取要存储元素的哈希值与 HashMap 不同的是,ConcurrentHashMap 不支持 null 键和 null 值。

2.确定 hash 槽 这一步就不多说了,同 HashMap 基本一致3.初始化底层数组存储 拿到 hash 槽以后就是存储元素,但是首次 put 元素时候会触发初始化底层数组操作,在 HashMap 中这一步操作是很简单的,因为是单线程操作直接初始化就可以了。

但是在 ConcurrentHashMap 中就需要考虑并发问题了,因为有可能有多个线程同时 put 元素这里就用到了我们之前文章中介绍的无锁编程利器 volatilSEO搜索优化论文e 和 CAS 原子操作每个线程初始化数组之前都会先获取到 volatile 修饰的 sizeCtl 变量,只有设置了这个变量的值才可以初始化数组,同时数组也是由 volatile 修饰的,以便修改后能被其他线程及时察觉。

不知道 volatile 和 CAS 算法的同学可以参考往期的文章 ReentranLock 实现原理居然是这样?具体初始化代码可参考 ConcurrentHashMap 的 initTable 方法3.插入元素 数组初始化结束后就可以开心的插入元素了,插入数组元素又分了如下 3 个情况:

3.1 当前槽中没有元素 这种情况最为简单,直接调用 Unsafe 封装好的 CAS 方SEO搜索优化论文法插入设置元素即可,成功则罢,不成功的话会转变为下个情况3.2 槽中已有元素 操作已有元素也有 2 个情况:3.2.1 槽中元素正在被迁移(ConcurrentHashMap 扩容操作) 如果当前槽的元素正在被扩容到新的数组当中,那么当前线程要帮忙迁移扩容。

3.2.3 槽中元素处于普通状态 这个时候就是和 HashMap 类似的 put 操作,特殊之处在于 ConcurrentHashMap 需要在此处加锁,因为可能当前有多个线程都想往这个槽上添加元素这里使用的 synchronized 锁,锁对象为当前槽的第一个元素。

锁住以后的操作就和 HashMap 类似了,如果槽中依然是列表形态,那么将SEO搜索优化论文当前元素包装为 Node 添加到队列尾部,插入元素结束后如果发现列表元素达到了 8, 则会将列表转换为二叉树(注意:插入元素后会释放锁,若发现要转换为二叉树,则会重新进行加锁操作)。

如果槽中元素已经转换为了平衡二叉树,那么将元素封装为 TreeNode 插入到树中4.扩容 在插入元素的末尾,ConcurrentHashMap 会计算当前容器内的元素,然后决定是否需要扩容扩容这一部分涉及到了比较复杂的运算,后面我们会单独出一遍文章来探讨分析,敬请关注。

至此相信你已经对 ConcurrentHashMap 可以支持并发的原理有了大致的了解,它主要依靠了 volatile 和 CAS 操作,再加上 SEO搜索优化论文synchronized 即实现了一个支持并发的哈希表了解了以上的内容和我们后面将要讨论的扩容话题基本可以对付面试了,当然啦 ConcurrentHashMap 的源码复杂程度远远高于 HashMap,对源码有兴趣的同学可以继续努力啃一啃,相信肯定会有更大的收获。

问题 5相信你一定记得学习并发编程的一个入门级例子,多个线程操作一个变量,累加 10000 次,最后结果居然不是 10000后来你把这个变量换成了并发包中的原子类型变量 AtomicLong,完美的解决了并发问题。

假如面试官问:还有更好的选择吗?LongAdder 了解过吗?你能对答如流吗?我的答案AtomicLong 是 Java SEO搜索优化论文1.5 并发包中提供的一个原子类,他提供给了我们在多线程环境下安全的并发操作一个整数的特性。

并且性能还可以,它主要依赖了 2 个技术,volatile 关键字和 CAS 原子指令,不知道这俩个技术的小伙伴参考往期的文章:ReentranLock 实现原理居然是这样?;AtomicLong 性能已经不错了,但是当在线程高度竞争的状况下性能会急剧下降,因为高度竞争下 CAS 操作会耗费大量的失败计算,因为当一个线程去更新变量时候发现值早已经被其他线程更新了。

那么有没有更好的解决方案呢,于是 LongAdder 诞生了LongAdder 是 Java 1.8 并发包中提供的一个工具类,它采用了一个分SEO搜索优化论文散热点数据的思路简单来说,Atomic 中所有线程都去更新内存中的一个变量,而 LongAdder 中会有一个 Cell 类型的数组,这个数组的长度是 CPU 的核心数,因为一台电脑中最多同时会有 CPU 核心数个线程并行运行,每个线程更新数据时候会被映射到一个 Cell 元素去更新,这样就将原本一个热点的数据,分散成了多个数据,降低了热点,这样也就减少了线程的竞争程度,同时也就提高了更新的效率。

当然这样也带了一个问题,就是更新函数不会返回更新后的值,而 AtomicLong 的更新方法会返回更新后的结果,LongAdder 只有在调用 sum 方法的时候才会去累加每个 Cell 中的数据,然SEO搜索优化论文后返回结果。

当然 LongAdder 中也用到了 volatile 和 CAS 原子操作,所以小伙伴们一定要掌握这俩个技术点,这是面试必问的点既然说 LongAdder 的效率更好,那我们就来一段测试代码,小小展示一下 LongAdder 的腻害之处,请看如下:

publicclassAtomicLongTester { privatestatic AtomicLong numA = new AtomicLong(0); private

static LongAdder numB = new LongAdder(); publicstaticvoiSEO搜索优化论文dmain(String[] args) throws InterruptedException

{ for (int i = 1; i < 10001; i*=10) { test(false, i); test(

true, i); } } publicstaticvoidtest(boolean isLongAdder, int threadCount) throws InterruptedException

{ long starTime = System.currentTimeSEO搜索优化论文Millis(); final CountDownLatch latch =

new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) {

new Thread(new Runnable() { publicvoidrun() {

for (int i = 0; i < 100000; i++) { if (isLongAdder) { numB.

add(1); } else { numA.addAndGeSEO搜索优化论文t(

1); } } latch.countDown(); } }).start(); }

// 等待所有运算结束 latch.await(); if (isLongAdder) { System.out.println(

"Thread Count=" + threadCount + ", LongAdder cost ms=" + (System.currentTimeMillis() - starTime) + ", Result="

+ numB.suSEO搜索优化论文m()); } else { System.out.println("Thread Count=" + threadCount +

", AtomicLong cost ms=" + (System.currentTimeMillis() - starTime) + ", Result=" + numA.get()); } numA =

new AtomicLong(0); numB = new LongAdder(); } } 实验结果大致如下:ThreadCount=SEO搜索优化论文1, AtomicLong cost ms=9, Result=100000

ThreadCount=1, LongAdder cost ms=13, Result=100000ThreadCount=10, AtomicLong cost ms=14, Result=1000000

ThreadCount=10, LongAdder cost ms=41, Result=1000000ThreadCount=100, AtomicLong cost ms=111, Result=10000000

ThreadCount=100, LongAdder cost ms=45, Result=10000SEO搜索优化论文000ThreadCount=1000, AtomicLong cost ms=1456, Result=100000000

ThreadCount=1000, LongAdder cost ms=379, Result=100000000ThreadCount=10000, AtomicLong cost ms=17452, Result=1000000000

ThreadCount=10000, LongAdder cost ms=3545, Result=1000000000从上面的结果可以看出来,当线程竞争率比较低的时候 AtomicLong 效率还是优于 LongAdder 的,但是当线SEO搜索优化论文程竞争率增大的时候,我们可以看出来 LongAdder 的性能远远高于 AtomicLong。

因此在使用原子类的时候,我们要结合实际情况,如果竞争率很高,那么建议使用 LongAdder 来替代 AtomicLong说到 LongAdder 也不得的说一说 volatile 带来的伪共享问题,对伪共享感兴趣的同学欢迎关注后续的文章,我们会在后续的文章中探讨这个问题。

SEO搜索优化论文le 修饰的 Cell 数组。

由于数组的连续存储特性,会存在伪共享问题,你知道 LongAdder 是如何解决的吗?我的答案在探讨 LongAdder 是如何解决伪共享问题之前,我们要先梳理清一个概念,什么是 伪共享和共享 ?共享在 Java 编程里面我们可以这样理解,有一个 Share 类,它有一个 value 的属性。

如下:publicclassShare { intvalue;} 我们初始化 Share 的一个实例,然后启动多个线程去操作它的 value 属性,此时的 Share 变量被多个线程操作的这种情况我们称之为

共享大家都知道在不添加任何互斥措施的情况,多线程操作这SEO搜索优化论文个 Share 变量的 value 属性肯定存在线程安全性的问题那有什么办法可以解决这个问题呢?我们可以使用 volatile 和 CAS 技术来保证共享变量可以安全的被多个线程共享操作使用,不知道 volatile 和 CAS 技术点的同学可以参考往期文章 ReentranLock 实现原理居然是这样?。

但是由于 volatile 的引入,会带来一些问题大家都知道 JMM(Java 内存模型)规范了 volatile 具有内存可见性和禁止指令重排序的语义这俩条语义使得某个线程更新本地缓存中的 value 值后会将其他线程的本地缓存中的 value 值失效,然后其他线程再次读取 value 值SEO搜索优化论文的时候需要去主存里面获取 value 值,这样即保证了 value 的内存可见性。

当然啦,这没有任何问题,但是由于线程本地缓存的操作是以缓存行为单位的,一个缓存行大小通常为 64B(不同型号的电脑缓存行大小会有不同)因此一个缓存行中不会只单单存储 value 一个变量,可能还会存储其他变量。

这样当一个线程更新了 value 之后,如果其他线程本地缓存中同样缓存了 value, value 所在的缓存行就会失效,这意味着该缓存行上的其他变量也会失效,那么线程对这个该缓存行上所有变量的访问都需要从主存中获取。

我们都知道 CPU 访问主存的速度相对于访问缓存的速度有着数量级的差距,这就带了很大的性能SEO搜索优化论文问题,我们将这个问题称之为 伪共享理解了伪共享到底是什么鬼以后,我们来看看 Java 大师们是怎么解决这个问题的。

在早期版本的 JDK 里面你应该见到过类似如下的代码:publicclassShare { volatileintvalue; long p1, p2, p3, p4, p5, p6; }

你可能猜到了,定义了几个无用的变量作为填充物,他们会保证一个缓存行里面只保存了 Share 变量,这样更新 Share 变量的时候就不会存在伪共享问题了但是这种方法存在什么问题呢?首先基于每台运行 Java 程序的机器的缓存行大小可能不同,其次由于这SEO搜索优化论文些类似填充物的变量并没有被实际使用,可以被 JVM 优化掉,这样就失效了。

基于此,在 Java 8 的时候,官方给出了解决策略,这就是 Contended 注解依赖于这个注解,我们在 Java 8 环境下可以这样改善代码:publicclassShare { @Contended 。

volatileintvalue; } 使用如上注解,并且在 JVM 启动参数中加入 -XX:-RestrictContended,这样 JVM 在运行时就会自动的为我们的 Share 类添加合适大小的填充物(padding)来解决伪共享问题,而不需要SEO搜索优化论文我们手写变量来作为填充物了,这样就更加便捷优雅的解决了伪共享问题。

悄悄的告诉你,LongAdder 就是使用 Contended 来解决伪共享问题哒好了,相信你已经了解了什么是伪共享问题,以及早期并发编程大师是如何解决伪共享问题的,最后我们也介绍了在 Java 8 中使用 Contended 来更优雅的解决伪共享问题。

Contended 还提供了一个缓存行分组的功能,在上文中我们没有介绍,欢迎有兴趣的小伙伴们自行探索吧(嘿嘿)问题 7少年,老衲看你骨骼清奇,眉宇之间透露着一股王者的气息来吧,跟我讲讲 ConcurrentHashMap 是如何进行管理它的容量的,也就是当我们调用它的 size SEO搜索优化论文方法的时候发生了什么故事?(前面我们介绍了 ConcurrentHashMap 的实现原理,但是扩容和容量大小留了个小尾巴,今天来割一下这个小尾巴,嘿嘿)。

我的答案毕竟是要支持并发,ConcurrentHashMap 的扩容操作比较复杂,我们将从以下几点来带讨论一下它的扩容触发扩容1.添加新元素后,元素个数达到扩容阈值触发扩容2.调用 putAll 方法,发现容量不足以容纳所有元素时候触发扩容。

3.某个槽内的链表长度达到 8,但是数组长度小于 64 时候触发扩容统计元素个数触发后面 2 点没啥问题,但是第 1 点中有个小问题,它是如何统计元素的个数呢?它采用和 LongAdder 类似的分散热SEO搜索优化论文点数据的解决思路,不知道 LongAdder 的小伙伴可以参考往期文章 还在用 AtomicLong?是时候了解一下 LongAdder 了。

ConcurrentHashMap 内部定义了一个 CounterCell 的类,它同样被 Contended 修饰防止 volatile 带来的伪共享问题,伪共享不了解可以参考往期文章 面试官问我 volatile 是否存在伪共享问题?我懵逼了。

内部实例化了一个 CounterCell 的数组来记录元素的个数,每当线程 put 一个元素到容器中,线程会被映射到一个 CounterCell 的一个元素上面采用 CAS 算法进行加 1 操作,当然如果当前 SEO搜索优化论文CounterCell 上已经有线程在操作,或者并发量比较小的话会直接将加 1 累加到 BASECOUNT 上面。

就是依据这样的策略,容器的元素的个数就会游刃有余的计算出来,当需要获取当前容器元素个数的时候,直接将 CounterCell 数据的每个元素值加起来再加上 BASECOUNT 的值就是当前容器中的元素个数。

扩容上面我们知道了触发扩容的条件为元素个数达到阈值开始扩容,然后也知道了它是如何统计元素的个数的,接下来就看看扩容的运行机制首先每个线程承担不小于 16 个槽中的元素的扩容,然后从右向左划分 16 个槽给当前线程去迁移,每当开始迁移一个槽中的元素的时候,线程会锁住当前槽中列表的头SEO搜索优化论文元素,假设这时候正好有 get 请求过来会仍旧在旧的列表中访问,如果是插入、修改、删除、合并、compute 等操作时遇到 ForwardingNode,当前线程会加入扩容大军帮忙一起扩容,扩容结束后再做元素的更新操作。

总结总结一下,对于 ConcurrentHashMap 的扩容我们需要明确如上三点就可以了,扩容触发的时机、元素个数的计算以及具体扩容过程中是如何坚持对外提供服务的就可以了如上即为 ConcurrentHashMap 扩容操作的简单介绍,实际 JDK 里面的扩容源码非常复杂,如果有小伙伴对源码感兴趣的话,本毛毛虫找到一篇不错的源码分析文章大家可以参考一下 。

https://blSEO搜索优化论文og.csdn.net/ZOKEKAI/article/details/90051567 这篇相当于 ConcurrentHashMap 的续集,对前序感兴趣的可以参考往期文章 ConcurrentHashMap 并发读写,用了什么黑科技。

以上即为昨天的问题的答案,小伙伴们对这个答案是否满意呢?欢迎留言和我讨论问题 8你用过 Java 中的 final 关键字吗?它有哪些作用?这也是出镜率极高的题目哦我的答案1.final 修饰类的时候代表这个类是不可以被扩展继承的,例如 JDK 里面的 String 类。

2.final 修饰方法的时候代表这个方法不能被子类重写3.final 修饰变量的时候代SEO搜索优化论文表这个变量一旦被赋值就不能再次被赋值想必上面这三点是大家所熟知的,但是下面这 2 点你想到了吗?缓存final 变量会被缓存在寄存器中而不需要去主从获取,而非 final 变量的则需要去主存重新加载。

线程可见性类的 final 域在编译器层面会保证在类的构造器结束之前一定要初始化完成,同时 Java 内存模型会保证对象实例化后它的 final 域对其他线程是可见的,然而非 final 域并没有这种待遇。

例如如下代码:publicclassFinalFiled{ finalint x; int y; static FinalFiled f; pubSEO搜索优化论文licFinalFiled

(){ x = 100; y = 100; } staticvoidwriter(){ f =

new FinalFiled(); } staticvoidreader(){ if (f != null) {

int i = f.x; // 保证此时一定是 100 int j = f.y; // 有可能此时还是 0 } } }

当线程 A 执行了 writer 方法后,SEO搜索优化论文有线程 B 会进入 f != null 成立条件的代码块,此时由于变量 x 是 final 修饰的,JMM 会保证 x 此时的值一定是 100,而 y 是非 final 的,则有可能此时 y 的值还是 0,并未被初始化。

安全性String 类的安全性也得益于恰到好处的使用了大量的 final 域,大家可以去翻翻 String 类的源码我们来举个例子,假设有线程 A 执行如下代码段:Global.flag = "/001/002"。

.substring(4);又有线程 B 执行如下代码段:String myS = Global.flag; if (myS.equals("/001SEO搜索优化论文")) System.out.println(myS);

如果没有 final 域的可见性保证,那么线程 B 在执行的时候有可能看到的字符串的长度可能仍然是 0当有了 final 域的可见性保证,就可以让并发程序正确的执行也使得 String 类成为名副其实不可变安全类。

问题 9利用 Java 中的动态绑定我们可以实现很多有意思的玩法例如Object obj = newString(); 集合类想必是小伙伴们低头不见抬头见的类了,那么你有没有想过集合类是否可以这样玩?List

list = new ArrayList(); 不瞒你说,这是毛毛虫在一次SEO搜索优化论文面试中遇到的真实的面试题,当时被问的一脸懵 如果是你,你知道怎么回答吗?我的答案首先我要告诉你的是,Java 集合类不允许这么玩,这样编译器会直接报错编译不通过。

我们都知道赋值一定需要是 is a 的关系,虽然 Object 和 Integer 是父子关系,但是很可惜 List

扫描二维码推送至手机访问。

版权声明:本文由海南拓宏网络科技工作室发布,如需转载请注明出处。

本文链接:http://4blc.com/post/13193.html

分享给朋友:

“什么是超高频皮肤整形术(什么是超高频RFID)” 的相关文章

java是什么百度百科(java是什么玩意)

本篇文章给大家谈谈 90后或者00后小时候如果用国产山寨机下过游戏的,想必会经常看见游戏开始页面上会有一个Java的字样和一杯咖啡的LOGO许多人会比较好奇,Java是什么意思?长大之后或许听说过Java是一种编程语言,但也会不禁思考Java是干什么用的?今天我来给没有接触过计算机...

kotlin发展前景(kotlin有前途吗)

今天给各位分享 整理 | 王启隆透过「历史上的今天」,从过去看未来,从现在亦可以改变未来今天是 2023 年 2 月 15 日,在 1564 年的今天,欧洲近代自然科学的创始人伽利略诞辰伽利略曾在学徒时期否定了亚里斯多德的重快轻慢的原理,随后他为了向全校师生证明自己提出的定律,在比...

java面向对象的三大特征及实现方法(java面向对象菜鸟教程)

今天给各位分享 Java基础-面向对象本文主要介绍Java OOP 面向对象基础和相关类图@Eason | GaoJava基础-面向对象三大特性封装继承多态类图泛化关系 (Generalization)实现关系 (Realization)。 聚合关系 (Aggregatio...

java是面向对象的程序设计语言(java是面向过程的与平台无关的网络编程语言)

今天给各位分享 JAVA是面向对象的两者的区别如下,面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行...

学java怎么学(学java要怎么学)

本文分享给大家的是: 软帝学院笔记Day8 面向对象(package关键字的概述及作用)(了解)A:为什么要有包将字节码(.class)进行分类存放包其实就是文件夹B:包的概述举例: 学生:增加,删除,修改,查询 老师:增加,删除,修改,查询 ... 方案1:按照功...

基于java的班主任管理系统(java学生管理系统简单)

今天给各位分享 ★ 老师介绍 ★ Alex亚马逊(美国)资深Java工程师Alex老师毕业于卡内基梅隆大学,现任职于亚马逊AWS(美国硅谷)从事云计算服务开发工作,前腾讯工程师擅长云计算,分布式系统,Web后端开发,信息安全现在主要学习和研究函数式编程和分布式系统相关领域。...