线程安全性 ---原子性 atomic
CPU 多级缓存: 缓存一致性,乱序执行优化
java内存模型:JMM规定,抽象结构,同步八种操作及规则
Java并发的优势与风险
1. 线程安全性
定义: 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。
线程安全主要表现在
1.原子性:提供了互斥访问,同一时刻只能有一个线程来对它进行操作
2.可见行:一个线程对主内存的修改可以及时的被其他线程观察到
3.有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序
------------------ 原子性 - Atomic 包----------
1. AtomicXXX: CAS,Unsafe.compareAndSwapInt
2. AtomicLong,LongAdder
3. AtomicReference,AtomicReferenceFieldUpdater
4. AtomicStampReference: 解决CAS 的ABA 问题
------------------原子性 - Atomic 包----------------
Class AtomicInteger
java.lang.Object
java.lang.Number
java.util.concurrent.atomic.AtomicInteger
一个可以自动更新的int值。看到java . util . concurrent。描述原子变量属性的原子包规范。AtomicInteger用于原子递增计数器等应用程序,不能用作整数的替换。但是,这个类确实扩展了Number,允许处理基于数字的类的工具和实用程序进行统一访问
用法:
AtomicInterger count = new AtomicInterger(0);
........
count.incrementAndGet();// 这里执行的是 count++ 操作
说明: 把原来的 int count = 0; 替换成 AtomicInterger count = new AtomicInterger(0); 就变成线程安全的了
底层源码:
在AtomicInteger这个类中
Class AtomicLong
java.lang.Object
java.lang.Number
java.util.concurrent.atomic.AtomicLong
可以自动更新的长值。看到java . util . concurrent。描述原子变量属性的原子包规范。AtomicLong用于原子递增的***等应用程序,并且不能用作Long的替换。但是,这个类确实扩展了Number,允许处理基于数字的类的工具和实用程序进行统一访问
就像我们所知道的那样,AtomicLong的原理是依靠底层的cas来保障原子性的更新数据,在要添加或者减少的时候,会使用死循环不断地cas到特定的值,从而达到更新数据的目的。
#--------------------------------------------------------------------------------------------------------------------------------------------------------------------#
Class LongAdder(jdk 1.8 产生LongAdder和DoubleAdder)
java.lang.Object
java.lang.Number
java.util.concurrent.atomic.LongAdder
一个或多个变量一起维持最初的零长和。当更新(方法add(long))在线程之间发生争用时,一组变量可能会动态增长以减少争用。方法sum()(或者,等效地,longValue())返回维护sum的变量之间的当前总和,
当多个线程更新用于收集统计信息等目的(而不是用于细粒度同步控制)的公共和时,此类通常比AtomicLong更可取。在低更新争用下,这两个类具有相似的特性。但是在高争用情况下,该类的预期吞吐量要高得多,这是以更高的空间消耗为代价的。
LongAdders可以与ConcurrentHashMap一起使用,以维护可伸缩的频率图(直方图或多集的一种形式)。例如,要向ConcurrentHashMap<String,LongAdder> freqs添加计数,如果还没有初始化,可以使用freqs.computeIfAbsent(k -> new LongAdder()).increment();
这个类扩展了Number,但没有定义equals、hashCode和compareTo等方法,因为预期实例会发生变化,所以作为集合键并不有用。
1.可以看到和AtomicLong基本类似,同样有增加、减少等操作,那么如何实现原子的增加呢?
我们可以看到一个Cell的类,那这个类是用来干什么的呢?
我们可以看到Cell类的内部是一个volatile的变量,然后更改这个变量唯一的方式通过cas。我们可以猜测到LongAdder的高明之处可能在于将之前单个节点的并发分散到各个节点的,这样从而提高在高并发时候的效率。
下面我们来验证我们的观点,我们接着看上图的add方法,如果cell数组不为空,那么就尝试更新base元素,如果更新成功,那么就直接返回。base元素在这里起到了一个什么作用呢?可以保障的是在低并发的时候和AtomicLong一样的直接对基础元素进行更新。
LongAdder总结:
LongAdder在AtomicLong的基础上将单点的更新压力分散到各个节点,在低并发的时候通过对base的直接更新可以很好的保障和AtomicLong的性能基本保持一致,而在高并发的时候通过分散提高了性能。
缺点是LongAdder在统计的时候如果有并发更新,可能导致统计的数据有误差。
Class AtomicBoolean
java.lang.Object
java.util.concurrent.atomic.AtomicBoolean
可以自动更新的布尔值。看到java . util . concurrent。描述原子变量属性的原子包规范。AtomicBoolean用于原子更新标志等应用程序中,不能用作Boolean的替换。
1. 在多项中可以 保证某段代码只执行一次
private AtomicBoolean isHappened = new AtomicBoolean(false);
public void test(){
if(isHappended.compareAndSet(false,true)){
// 这里只执行一次
}
}
:: AtomicBoolean 说明::
AtomicBoolean是Java.util.concurrent.atomic包下的原子变量,这个包里面提供了一组原子类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。实际上是借助硬件的相关指令来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)。
例如AtomicBoolean,在这个Boolean值的变化的时候不允许在之间插入,保持操作的原子性。方法和举例:compareAndSet(boolean expect, boolean update)。这个方法主要两个作用
比较AtomicBoolean和expect的值,如果一致,执行方法内的语句。其实就是一个if语句
把AtomicBoolean的值设成update
比较最要的是这两件事是一气呵成的,这连个动作之间不会被打断,任何内部或者外部的语句都不可能在两个动作之间运行。为多线程的控制提供了解决的方案。
Class AtomicReference < V>
java.lang.Object
java.util.concurrent.atomic.AtomicReference
可以自动更新的对象引用。看到java . util . concurrent。描述原子变量属性的原子包规范。
用法:
private static AtomicReference< Integer> count = new AtomicReference< Integer>(0);
public static void main(String[ ]args){
count.compareAndSet(0,2); // 执行 count=2
count.compareAndSet(0,1); //0!=2 不执行
count.compareAndSet(1,3); //1!=2不执行
count.compareAndSet(2,4);// 执行count=4
count.compareAndSet(3,5);//不执行
System.out.println(count.get());//===4 }
执行结果count的值为4
Class AtomicReferenceFieldUpdater<T,V>
java.lang.Object
java.util.concurrent.atomic.AtomicReferenceFieldUpdater<T,V>
一个基于反射的实用程序,支持对指定类的指定易失性引用字段进行原子更新。该类设计用于原子数据结构中,在原子数据结构中,同一节点的多个引用字段独立地服从原子更新
public class AtomicExample {
private static AtomicIntegerFieldUpdater<AtomicExample> updater =
AtomicIntegerFieldUpdater.newUpdater(AtomicExample.class,"count");
public volatile int count =100;// 必须用volatile 修饰,非static 修饰的
private static AtomicExample example = new AtomicExample();
public static void main(String [] args){
//如果count 的值为100 修改为200
if(updater.compareAndSet(example, 100, 200)){
System.out.println("1.修改成功count的值"+example.count);
}
if(updater.compareAndSet(example, 100, 200)){
System.out.println("2.修改成功count的值"+example.count);
}else {
System.out.println("修改失败count的值"+example.count);
}
}
}
输出结果;
- 修改成功count的值200
修改失败count的值200
Class AtomicStampedReference
java.lang.Object
java.util.concurrent.atomic.AtomicStampedReference < V>
AtomicStampedReference维护一个对象引用和一个整数“stamp”,后者可以自动更新。
实现注意:此实现通过创建表示“已装箱”[reference, integer]对的内部对象来维护已盖戳的引用。