1. ----java.util.concurrent 用于并发编程的的公共工具类。
  2. --------java.util.concurrent.atomic 支持单一变量无锁和线程安全的小工具类。
  3. --------java.util.concurrent.locks 为锁定和等待不同的内置同步和监视器提供一个框架的类和接口。
  4. ----java.util.function 为微积分表达式和方法引用提供目标类型的功能接口。
  5. ----java.util.jar 提供了读写JARJava归档)文件格式,它是基于标准的的ZIP文件格式和一个可选清单文件的。
  6. ----java.util.logging JavaTM2平台核心日志工具提供了类和接口。
  7. ----java.util.prefs 此包允许应用程序存储并检索用户和系统首选项和配置数据。
  8. ----java.util.regex 用于匹配违反了正则表达式指定模式的字符序列的类。
  9. ----java.util.spi java.util包的服务提供者类。
  10. ----java.util.stream 支持在如集合多核处理转换这样的元素流上的功能样式操作的类。
  11. ----java.util.zip 提供了读写标准的ZIPGZIP文件格式的类。

重点看

  1. ----java.util.concurrent 用于并发编程的的公共工具类。
  2. --------java.util.concurrent.atomic 支持单一变量无锁和线程安全的小工具类。
  3. --------java.util.concurrent.locks 为锁定和等待不同的内置同步和监视器提供一个框架的类和接口。

java.util.concurrent.atomic

定义

参考地址:https://blog.csdn.net/lmb55/article/details/79547685
java.util.concurrent.atomic中的类可以分成4组:
**

  1. 标量类:AtomicBooleanAtomicIntegerAtomicLongAtomicReference
  2. 数组类:AtomicIntegerArrayAtomicLongArrayAtomicReferenceArray
  3. 更新器类:AtomicLongFieldUpdaterAtomicIntegerFieldUpdaterAtomicReferenceFieldUpdater
  4. 复合变量类:AtomicMarkableReferenceAtomicStampedReference

一、标量类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
**
AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference这四种基本类型用来处理布尔,整数,长整数,对象四种数据,其内部实现不是简单的使用synchronized,而是一个更为高效的方式CAS (compare and swap) + volatile和native方法,从而避免了synchronized的高开销,执行效率大为提升。其实例各自提供对相应类型单个变量的访问和更新。每个类也为该类型提供适当的实用工具方法。

1、set()和get()方法可以原子的设定和获取atomic的数据,利用 volatile 定义 value 值,保证数据会在主存中设置或读取。

2、void set()和void lazySet():
效果:set设置为给定值,直接修改原始值;lazySet延时设置变量值,
实现原理:lazySet是通过一个native 方法 setOrderedInt ( 其他的类型 是:setOrderedXXX 方法),这个方法的实现逻辑是,按照正常的变量赋值方式,不开启 内存屏障(内存屏障:就是在设置和读取时候,强制内存失效,强制到主存储中读取值或者设置值),所以如果不是想立即读取设置的新值, 可以使用此方法,
另外这个方法很少被用到。但是有些场景,比如外面用到锁,而内部用的是volatile 字段修饰的,这样就需要 优化volatile 的字段的使用。避免一些不必要的 内存屏障的开销。

3、getAndSet()方法
作用:原子的将变量设定为新数据,同时返回先前的旧数据。
实现方式:他依赖 getIntVolatile 和 compareAndSwapInt 来实现的。

  1. compareAndSwapInt() // native 方法
  2. 2个方法接受2个参数,一个是期望数据(expected),一个是新数据(new);
  3. 如果atomic里面的数据和期望数据一 致,
  4. 则将新数据设定给atomic的数据,
  5. 返回true,表明成功;否则就不设定,并返回false

其中,期望值参数,传入的是当前对象自己的当前值:valueOffset
所以 compareAndSwapInt 判断后就会更新自己的值为预期值。
大体上的实现 等于 get 后 set 操作。

4、compareAndSet()和weakCompareAndSet()
相同点:这两个方法都是conditional modifier 条件更新方法。这2个方法接受2个参数,一个是期望数据(expected),一个是新数据(new);如果atomic里面的数据和期望数据一 致,则将新数据设定给atomic的数据,返回true,表明成功;否则就不设定,并返回false
不同点:
JDK规范中说:以原子方式读取和有条件地写入变量但不 创建任何 happen-before 排序,因此不提供与除 weakCompareAndSet 目标外任何变量以前或后续读取或写入操作有关的任何保证。

大意就是说调用weakCompareAndSet时并不能保证不存在happen- before的发生(也就是可能存在指令重排序导致此操作失败)。但是从Java源码来看,其实此方法并没有实现JDK规范的要求,最后效果和 compareAndSet是等效的,都调用了unsafe.compareAndSwapInt()完成操作。

AtomicReference**
将 自定义的对象,设置为 atomic可以进行原子性操作。 在获取和修改时候进行线性操作。
其中主要的 用法:

  1. 实现: AtomicReference<V> 一个泛型 V
  2. 里面定义 volatile V 再进行操作。与 atomicIntenger 等相同。
  3. get 设置
  4. set 获取
  5. getAndSet 现获取到 old 值,然后设置。
  6. compareAndSet 比较如果等于期望值,则更新成目标值

cas 的总结:
https://www.cnblogs.com/Leo_wl/p/6899716.html
CAS缺点
CAS虽然很高效的解决原子操作,但是CAS仍然存在三大问题。ABA问题,循环时间长开销大和只能保证一个共享变量的原子操作

  1. ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。
    (我注:因为cas 比对的是值。所以如果这个值正在有别的线程操作。只是操作到 从值A到B又到 A ,这个时候我们进行了cas 操作。此时 另外的线程又开始将 A 转变为 C ,其实并没有达到 互斥线程 的作用)

从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
关于ABA问题参考文档: http://blog.hesey.NET/2011/09/resolve-aba-by-atomicstampedreference.html

2. 循环时间长开销大。自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。

3. **只能保证一个共享变量的原子操作。当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。

2、比较花费CPU资源,即使没有任何争用也会做一些无用功。

3、会增加程序测试的复杂度,稍不注意就会出现问题。

总结

可以用CAS在无锁的情况下实现原子操作,但要明确应用场合,非常简单的操作且又不想引入锁可以考虑使用CAS操作,当想要非阻塞地完成某一操作也可以考虑CAS。不推荐在复杂操作中引入CAS,会使程序可读性变差,且难以测试,同时会出现ABA问题。
**

面试题





**