CAS 从语义上来说存在一个逻辑漏洞:如果 V 初次读取时是 A,并且在准备赋值时仍为 A,这依旧不能说明它没有被其他线程更改过,因为这段时间内假设它的值先改为 B 又改回 A,那么 CAS 操作就会误认为它从来没有被改变过。
这个漏洞称为 ABA 问题,juc 包提供了一个 AtomicStampedReference,原子更新带有版本号的引用类型,通过控制变量值的版本来解决 ABA 问题。大部分情况下 ABA 不会影响程序并发的正确性,如果需要解决,传统的互斥同步可能会比原子类更高效。
Q3:有哪些原子类?JDK 5 提供了 java.util.concurrent.atomic 包,这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。到 JDK 8 该包共有17个类,依据作用分为四种:原子更新基本类型类、原子更新数组类、原子更新引用类以及原子更新字段类,atomic 包里的类基本都是使用 Unsafe 实现的包装类。
AtomicInteger 原子更新整形、 AtomicLong 原子更新长整型、AtomicBoolean 原子更新布尔类型。
AtomicIntegerArray,原子更新整形数组里的元素、 AtomicLongArray 原子更新长整型数组里的元素、 AtomicReferenceArray 原子更新引用类型数组里的元素。
AtomicReference 原子更新引用类型、AtomicMarkableReference 原子更新带有标记位的引用类型,可以绑定一个 boolean 标记、 AtomicStampedReference 原子更新带有版本号的引用类型,关联一个整数值作为版本号,解决 ABA 问题。
AtomicIntegerFieldUpdater 原子更新整形字段的更新器、 AtomicLongFieldUpdater 原子更新长整形字段的更新器AtomicReferenceFieldUpdater 原子更新引用类型字段的更新器。
Q4:AtomicIntger 实现原子更新的原理是什么?AtomicInteger 原子更新整形、 AtomicLong 原子更新长整型、AtomicBoolean 原子更新布尔类型。
getAndIncrement 以原子方式将当前的值加 1,首先在 for 死循环中取得 AtomicInteger 里存储的数值,第二步对 AtomicInteger 当前的值加 1 ,第三步调用 compareAndSet 方法进行原子更新,先检查当前数值是否等于 expect,如果等于则说明当前值没有被其他线程修改,则将值更新为 next,否则会更新失败返回 false,程序会进入 for 循环重新进行 compareAndSet 操作。
atomic 包中只提供了三种基本类型的原子更新,atomic 包里的类基本都是使用 Unsafe 实现的,Unsafe 只提供三种 CAS 方法:compareAndSwapInt、compareAndSwapLong 和 compareAndSwapObject,例如原子更新 Boolean 是先转成整形再使用 compareAndSwapInt 。
Q5:CountDownLatch 是什么?CountDownLatch 是基于执行时间的同步类,允许一个或多个线程等待其他线程完成操作,构造方法接收一个 int 参数作为计数器,如果要等待 n 个点就传入 n。每次调用 countDown 方法时计数器减 1,await 方***阻塞当前线程直到计数器变为0,由于 countDown 方法可用在任何地方,所以 n 个点既可以是 n 个线程也可以是一个线程里的 n 个执行步骤。
Q6: CyclicBarrier 是什么?循环屏障是基于同步到达某个点的信号量触发机制,作用是让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障才会解除。构造方法中的参数表示拦截线程数量,每个线程调用 await 方法告诉 CyclicBarrier 自己已到达屏障,然后被阻塞。还支持在构造方法中传入一个 Runnable 任务,当线程到达屏障时会优先执行该任务。适用于多线程计算数据,最后合并计算结果的应用场景。
CountDownLacth 的计数器只能用一次,而 CyclicBarrier 的计数器可使用 reset 方法重置,所以 CyclicBarrier 能处理更为复杂的业务场景,例如计算错误时可用重置计数器重新计算。
Q7:Semaphore 是什么?信号量用来控制同时访问特定资源的线程数量,通过协调各个线程以保证合理使用公共资源。信号量可以用于流量控制,特别是公共资源有限的应用场景,比如数据库连接。
Semaphore 的构造方法参数接收一个 int 值,表示可用的许可数量即最大并发数。使用 acquire 方法获得一个许可证,使用 release 方法归还许可,还可以用 tryAcquire 尝试获得许可。
Q8: Exchanger 是什么?交换者是用于线程间协作的工具类,用于进行线程间的数据交换。它提供一个同步点,在这个同步点两个线程可以交换彼此的数据。
两个线程通过 exchange 方法交换数据,第一个线程执行 exchange 方法后会阻塞等待第二个线程执行该方法,当两个线程都到达同步点时这两个线程就可以交换数据,将本线程生产出的数据传递给对方。应用场景包括遗传算法、校对工作等。
P9:JDK7 的 ConcurrentHashMap 原理?