线程安全性 ---原子性 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这个类中

线程安全性 ---原子性 atomic
线程安全性 ---原子性 atomic
线程安全性 ---原子性 atomic

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基本类似,同样有增加、减少等操作,那么如何实现原子的增加呢?

线程安全性 ---原子性 atomic
我们可以看到一个Cell的类,那这个类是用来干什么的呢?
线程安全性 ---原子性 atomic
我们可以看到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的替换。
线程安全性 ---原子性 atomic

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);
    }
}

}
输出结果;

  1. 修改成功count的值200
    修改失败count的值200

Class AtomicStampedReference

java.lang.Object
java.util.concurrent.atomic.AtomicStampedReference < V>
AtomicStampedReference维护一个对象引用和一个整数“stamp”,后者可以自动更新。
实现注意:此实现通过创建表示“已装箱”[reference, integer]对的内部对象来维护已盖戳的引用。

AtomicStampReference: 解决CAS 的ABA 问题

ABA 问题: 在CAS 操作的时候,其他线程将 将变量 A 的值 改成了B 又改回了A,本线程 使用期望值A 与当前变量进行比较的时候法向A的值没有变,于是将A进行了操作,这个时候实际上A值已经被其他线程改变过。与设计思想不符合

AtomicStampedReference主要维护包含一个对象引用以及一个可以自动更新的整数”stamp”的pair对象来解决ABA问题。