java并发包中的原子操作类,这些类都是基于非阻塞算法CAS实现的。

4.1原子变量操作类

  AtomicInteger/AtomicLong/AtomicBoolean等原子操作类

  AtomicLong类:

public class AtomicLong extends Number implements java.io.Serializable {
// 基于硬件的原子操作类
private static final Unsafe unsafe = Unsafe.getUnsafe();
// 存放value的偏移地址
private static final long valueOffset;
//判断jvm是否支持Long类型无锁CAS
static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
private static native boolean VMSupportsCS8();
// 初始化value字段的偏移量
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicLong.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
   // 存放具体的值
private volatile long value;
.........
}

虽然该类提供了原子操作(虽然是无阻塞的CAS操作,相对于阻塞算法提升了很火),但是在高并发情况下,会竞争更新同一个原子变量,仍然会有效率问题。

导致问题的原因也就是CAS操作:

public  final  long  getAndAddLong(Object  paramObject ,  long  paramLongl ,  long paramLong2)
long l ;
do {
1 = getLongvolatile(paramObject , paramLongl) ;
// .compareAndSwapLong比较交换操作,该方法就是CAS的核心方法,但是该方法在高并发情况下,会竞争更新原子变量,最终只有一个线程更新成功,其他线程会循环多次CAS操作,浪费了cpu资源,降低了效率
) while (!compareAndSwapLong(param0bject , paramLongl , 1, 1 + paramLong2) ); return l ;
}
compareAndSwapLong:paramObject操作对象,paramLong1 value的偏移量,第三个参数为except表示当前是否是该值,最后一个参数就是要修改成的值

jdk8新增的原子操作类LongAdder

  为了解决高并发情况下多线程对一个共享变量的CAS争夺失败后进行自旋而造成的降低并发性能的问题,LongAdder在内部维护了一个cell元素(一个动态的cell数组)来分担对单个变量进行争夺的开销,也就是将对一个变量的争夺分配到对多个变量的争夺上。

LongAdder结构:继承Striped64

Striped64类,Cell类上有一个Contented注解,作用是避免伪共享问题

  // 一个内部类,LongAdder就是通过Cell对象来提高性能,降低自选的CAS操作
@sun.misc.Contended static final class Cell {
volatile long value;
Cell(long x) { value = x; }
final boolean cas(long cmp, long val) {
return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
} // Unsafe类,基于硬件的原子操作类
private static final sun.misc.Unsafe UNSAFE;
// value在Cell对象内存地址中的偏移量
private static final long valueOffset;
// 静态代码块 初始化Unsafe类,和偏移量valueOfset
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> ak = Cell.class;
valueOffset = UNSAFE.objectFieldOffset
(ak.getDeclaredField("value"));
} catch (Exception e) {
throw new Error(e);
}
}
} /** Number of CPUS, to place bound on table size */
static final int NCPU = Runtime.getRuntime().availableProcessors(); // cell数组,每一个线程会争夺一个cell对象,cell数组变相的降低了争夺一个变量的性能问题
transient volatile Cell[] cells; // LongAdder的真实值就是base+cell[0]...+cell[n]
transient volatile long base; // 为了实现自旋锁,状态值只有0和1
transient volatile int cellsBusy;

LongAdder重点方法add():

  当内部increment或decrement自增或自减操作是,内部调用的就是add方法,当cell数组为空时,默认走的就是casBase方法(此时和AtomicLong操作相同,即都是在base的基础上进行累加或累减操作的)。

  进入第3行if判断的情况:

    1:cells不为空,会进入if

      假如cells不为空进入的if判断,则第5行结果为false,第6行会判断Thread对象中的threadLocalRandomProbe的值是否存在,不存在,则直接调用longAccumulate方法,存在的话,会进一步判断a.cas操作是否成功,不成功则调用longAccumulate方法,成功则代表add方法累加成功

    2:casBase操作失败,会进入if

      如果cells为空,casBase方法操作失败,则进入if判断中,且第5行为true,则直接调用longAccumulate方法

     public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
if ((as = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
longAccumulate(x, null, uncontended);
}
} /**
* Equivalent to {@code add(1)}.
*/
public void increment() {
add(1L);
} /**
* Equivalent to {@code add(-1)}.
*/
public void decrement() {
add(-1L);
}

longAccumulate方法是进行cells数组初始化和扩容的地方

    final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
int h;
// 初始化probe,也就是Thread类中的threadlocalsRandomProbe变量
if ((h = getProbe()) == 0) {
ThreadLocalRandom.current(); // force initialization
h = getProbe();
wasUncontended = true;
}
boolean collide = false; // True if last slot nonempty
// 死循环
for (;;) {
Cell[] as; Cell a; int n; long v;
// 这里和上边add方法的两种情况对应,一种是cells不为null,一种是未null,当前if为cells不为null的时候会进入
if ((as = cells) != null && (n = as.length) > 0) {
// as[(n-1) & h] 里边的索引和上一步add方法中的相同,都是通过probe的值 并上 cells数组的长度减1, 这一块就是获取线程应该访问的Cell对象(相当于AtomicLong对象中的共享变量)
if ((a = as[(n - 1) & h]) == null) {
if (cellsBusy == 0) { // Try to attach new Cell 当前索引处无Cell对象,并且没有线程在执行CAS操作,则会新建一个Cell对象
Cell r = new Cell(x); // Optimistically create
if (cellsBusy == 0 && casCellsBusy()) {
boolean created = false;
try { // Recheck under lock
Cell[] rs; int m, j;
if ((rs = cells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) {
rs[j] = r;
created = true;
}
} finally {
cellsBusy = 0;
}
if (created)
break;
continue; // Slot is now non-empty
}
}
collide = false;
}
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
else if (a.cas(v = a.value, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break;
else if (n >= NCPU || cells != as)
collide = false; // At max size or stale
else if (!collide)
collide = true;
else if (cellsBusy == 0 && casCellsBusy()) {
try {
if (cells == as) { // Expand table unless stale
Cell[] rs = new Cell[n << 1];
for (int i = 0; i < n; ++i)
rs[i] = as[i];
cells = rs;
}
} finally {
cellsBusy = 0;
}
collide = false;
continue; // Retry with expanded table
}
h = advanceProbe(h);
}
// 以下判断是当cells为null时,并且cellsBusy为0,并且能够将cellsBusy自增为1时,也就是能够加锁时,进入判断内
else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
boolean init = false;
try { // Initialize table
if (cells == as) { // 默认初始化Cell数组大小为2
Cell[] rs = new Cell[2];
rs[h & 1] = new Cell(x);
cells = rs;
init = true;
}
} finally {
cellsBusy = 0;
}
if (init)
break;
}
// 以上条件都不满足,如果指定了operation方法,则使用operation进行操作,没有的话,则进行累加操作(这块是针对自定义operation的)
else if (casBase(v = base, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break; // Fall back on using base
}
}

LongAccumulator类:LongAdder是LongAccumulator的一个特例,后者提供了更加强大的功能,可以让用户自定义累加规则

    public LongAccumulator(LongBinaryOperator accumulatorFunction,
long identity) {
this.function = accumulatorFunction;
base = this.identity = identity;
} // java8中的函数式接口可以自定义操作规则,累加、累乘等操作
@FunctionalInterface
public interface LongBinaryOperator { /**
* Applies this operator to the given operands.
*
* @param left the first operand
* @param right the second operand
* @return the operator result
*/
long applyAsLong(long left, long right);
}

java并发编程之美-阅读记录4的更多相关文章

  1. java并发编程之美-阅读记录1

    1.1什么是线程? 在理解线程之前先要明白什么是进程,因为线程是进程中的一个实体.(线程是不会独立存在的) 进程:是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程中的 ...

  2. java并发编程之美-阅读记录11

    java并发编程实践 11.1ArrayBlockingQueue的使用 有关logback异步日志打印中的ArrayBlockingQueue的使用 1.异步日志打印模型概述 在高并发.高流量并且响 ...

  3. java并发编程之美-阅读记录2

    2.1什么是多线程并发编程 并发:是指在同一时间段内,多个任务同时在执行,并且执行没有结束(同一时间段又包括多个单位时间,也就是说一个cpu执行多个任务) 并行:是指在单位时间内多个任务在同时执行(也 ...

  4. java并发编程之美-阅读记录10

    同步器 10.1CountDownLatch 在开发过程中经常会遇到在主线程中开启多个子线程去并行执行任务,并且主线程需要等待子线程执行完毕后在进行汇总.在CountDownLatch出现之前使用线程 ...

  5. java并发编程之美-阅读记录7

    java并发包中的并发队列 7.1ConcurrentLinkedQueue 线程安全的无界非阻塞队列(非阻塞队列使用CAS非阻塞算法实现),其底层数组使用单向列表实现,对于出队和入队操作使用CAS非 ...

  6. java并发编程之美-阅读记录6

    java并发包中锁 6.1LockSupport工具类 该类的主要作用就是挂起和唤醒线程,该工具类是创建锁和其他工具类的基础.LockSupport类与每个使用他的线程都关联一个许可证,在默认情况下调 ...

  7. java并发编程之美-阅读记录5

    java并发包中的并发List 5.1CopeOnWriteArrayList 并发包中的并发List只有CopyOnWriteArrayList,该类是一个线程安全的arraylist,对其进行的修 ...

  8. java并发编程之美-阅读记录3

    java并发包中的ThreadLocalRandom类,jdk1.7增加的随机数生成器 Random类的缺点:是多个线程使用同一个原子性的种子变量,导致对原子变量的更新产生竞争,降低了效率(该类是线程 ...

  9. Java并发编程之美之并发编程线程基础

    什么是线程 进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,一个进程至少有一个线程,进程的多个线程共享进程的资源. java启动main函数其实就 ...

随机推荐

  1. 2018-8-10-win10-UWP-发邮件

    title author date CreateTime categories win10 UWP 发邮件 lindexi 2018-08-10 19:17:19 +0800 2018-2-13 17 ...

  2. 全文检索引擎sphinx 与 Elasticsearch 索引速度对比

    sphinx的特色之一是建立索引速度快,最近转投Elasticsearch后,一直想做个对比,网上资料常见说法是10倍的差距. 测试环境 硬件:单核,2G内存的E5-2630 虚拟机 操作系统:Cen ...

  3. day02 循环、格式化输出、运算符、编码

    01 昨日内容回顾 python2x python3x区别: python2x:源码重复,不规范. python3x:源码规范,优美,清晰,简单. 编译型:将代码一次性全部转化成字节码. 代表语言:C ...

  4. Linux系统常用知识(centos7)

    一.查看系统版本 1.查看linux内核版本 #cat /etc/redhat-release 二.主机名 2.1定义: 静态的(Static hostname):“静态”主机名也称为内核主机名,是系 ...

  5. vue之路由传参三种基本方式

    现有如下场景,点击父组件的li元素跳转到子组件中,并携带参数,便于子组件获取数据. 父组件中: <li v-for="article in articles" @click= ...

  6. 【串线篇】Mybatis缓存之二级缓存

    1.应用 二级缓存:namespace级别的缓存:SqlSession关闭或者提交以后有效 一级缓存:SqlSession关闭或者提交以后,一级缓存的数据会放在二级缓存中: 二级缓存的使用:mybat ...

  7. 异常 Cannot resolve class or package

    spring boot yml配置异常Cannot resolve class or package 是因为mvaen设置 pom.xml的文件配置如上,scope 范围指定为runtime,runt ...

  8. IO流 读写文件

    读写文件 如前所述,一个流被定义为一个数据序列.输入流用于从源读取数据,输出流用于向目标写数据. 下图是一个描述输入流和输出流的类层次图. 下面将要讨论的两个重要的流是 FileInputStream ...

  9. Testng的使用总结(内容待持续更新)

    testng 6.8使用多线程时,在pom的surefire插件始终无效 -->升级testng版本,在6.8版本中无任何提示的 如何调用自定义的报告的 -->在testng中,有个IRe ...

  10. php strnatcasecmp()函数 语法

    php strnatcasecmp()函数 语法 作用:使用"自然"算法来比较两个字符串(不区分大小写):直线电机优势 语法:strnatcasecmp(string1,strin ...