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. query_module - 向内核查询和模块有关的各个位

    总览 #include <linux/module.h> int query_module(const char *name, int which,void *buf, size_t bu ...

  2. 微信小程序(14)--上传图片公用组件(父子传参)

    这周整理了一下做微信小程序页面时遇到的一些问题,先说说常见的上传图片吧. 上传图片公用组件 首先要了解的是父子传参. 1.A组件为父组件,B组件为子组件,以下是A组件向B组件传参: 在A组件的json ...

  3. vue,一路走来(1)--构建vue项目

    2016年12月--2017年5月,接触前端框架vue,一路走来,觉得有必要把遇到的问题记录下来. 那时,vux用的是1.0的vue,然而vue2.0已经出来了,于是我结合了mint-ui一起来做项目 ...

  4. Sass-乘法

    Sass 中的乘法运算和前面介绍的加法与减法运算还略有不同.虽然他也能够支持多种单位(比如 em ,px , %),但当一个单位同时声明两个值时会有问题.比如下面的示例: 编译的时候报“20px*px ...

  5. 07.整合jsp、整合freemarker、整合thymeleaf

    整合jsp pom.xml部分内容 <packaging>war</packaging> </dependencies> <dependency> &l ...

  6. Pycharm的debug单步调试

    首先设置断点,点击行号显示的一侧,在指定行设置断点.比如这里,我们设断点在创建对象时: .如果这时我们直接右键run这个代码.他还是会直接运行,不会理会断点 结果 所以要右键选择run下面的debug ...

  7. dubbo所用到的技术

    服务注册:zookeeper 协议:dubbo  Hessian  Rmi 网络编程:netty 动态代理:jdk和Javassist 序列化:Hessian  Dubbo  Json  Java S ...

  8. linux学习-文件管理

    1.文件系统结构 /boot:引导文件存放目录,内核文件(vmlinuz).引导加载器(bootloader, grub)都存放于此目录 /bin:所有用户使用的基本命令:不能关联至独立分区,OS启动 ...

  9. 数据挖掘:WAP-Tree与PLWAP-Tree

    简介 我们首先应该从WAP-Tree说起,下面一段话摘自<Effective Web Log Mining using WAP Tree-Mine>原文 Abstract -World W ...

  10. java 序列化机制

    package stream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io ...