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

4.1原子变量操作类

  AtomicInteger/AtomicLong/AtomicBoolean等原子操作类

  AtomicLong类:

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

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

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

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

jdk8新增的原子操作类LongAdder

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

LongAdder结构:继承Striped64

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

  1. // 一个内部类,LongAdder就是通过Cell对象来提高性能,降低自选的CAS操作
  2. @sun.misc.Contended static final class Cell {
  3. volatile long value;
  4. Cell(long x) { value = x; }
  5. final boolean cas(long cmp, long val) {
  6. return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
  7. }
  8.  
  9. // Unsafe类,基于硬件的原子操作类
  10. private static final sun.misc.Unsafe UNSAFE;
  11. // value在Cell对象内存地址中的偏移量
  12. private static final long valueOffset;
  13. // 静态代码块 初始化Unsafe类,和偏移量valueOfset
  14. static {
  15. try {
  16. UNSAFE = sun.misc.Unsafe.getUnsafe();
  17. Class<?> ak = Cell.class;
  18. valueOffset = UNSAFE.objectFieldOffset
  19. (ak.getDeclaredField("value"));
  20. } catch (Exception e) {
  21. throw new Error(e);
  22. }
  23. }
  24. }
  25.  
  26. /** Number of CPUS, to place bound on table size */
  27. static final int NCPU = Runtime.getRuntime().availableProcessors();
  28.  
  29. // cell数组,每一个线程会争夺一个cell对象,cell数组变相的降低了争夺一个变量的性能问题
  30. transient volatile Cell[] cells;
  31.  
  32. // LongAdder的真实值就是base+cell[0]...+cell[n]
  33. transient volatile long base;
  34.  
  35. // 为了实现自旋锁,状态值只有0和1
  36. 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方法

  1. public void add(long x) {
  2. Cell[] as; long b, v; int m; Cell a;
  3. if ((as = cells) != null || !casBase(b = base, b + x)) {
  4. boolean uncontended = true;
  5. if (as == null || (m = as.length - 1) < 0 ||
  6. (a = as[getProbe() & m]) == null ||
  7. !(uncontended = a.cas(v = a.value, v + x)))
  8. longAccumulate(x, null, uncontended);
  9. }
  10. }
  11.  
  12. /**
  13. * Equivalent to {@code add(1)}.
  14. */
  15. public void increment() {
  16. add(1L);
  17. }
  18.  
  19. /**
  20. * Equivalent to {@code add(-1)}.
  21. */
  22. public void decrement() {
  23. add(-1L);
  24. }

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

  1. final void longAccumulate(long x, LongBinaryOperator fn,
  2. boolean wasUncontended) {
  3. int h;
  4. // 初始化probe,也就是Thread类中的threadlocalsRandomProbe变量
  5. if ((h = getProbe()) == 0) {
  6. ThreadLocalRandom.current(); // force initialization
  7. h = getProbe();
  8. wasUncontended = true;
  9. }
  10. boolean collide = false; // True if last slot nonempty
  11. // 死循环
  12. for (;;) {
  13. Cell[] as; Cell a; int n; long v;
  14. // 这里和上边add方法的两种情况对应,一种是cells不为null,一种是未null,当前if为cells不为null的时候会进入
  15. if ((as = cells) != null && (n = as.length) > 0) {
    // as[(n-1) & h] 里边的索引和上一步add方法中的相同,都是通过probe的值 并上 cells数组的长度减1, 这一块就是获取线程应该访问的Cell对象(相当于AtomicLong对象中的共享变量)
  16. if ((a = as[(n - 1) & h]) == null) {
  17. if (cellsBusy == 0) { // Try to attach new Cell 当前索引处无Cell对象,并且没有线程在执行CAS操作,则会新建一个Cell对象
  18. Cell r = new Cell(x); // Optimistically create
  19. if (cellsBusy == 0 && casCellsBusy()) {
  20. boolean created = false;
  21. try { // Recheck under lock
  22. Cell[] rs; int m, j;
  23. if ((rs = cells) != null &&
  24. (m = rs.length) > 0 &&
  25. rs[j = (m - 1) & h] == null) {
  26. rs[j] = r;
  27. created = true;
  28. }
  29. } finally {
  30. cellsBusy = 0;
  31. }
  32. if (created)
  33. break;
  34. continue; // Slot is now non-empty
  35. }
  36. }
  37. collide = false;
  38. }
  39. else if (!wasUncontended) // CAS already known to fail
  40. wasUncontended = true; // Continue after rehash
  41. else if (a.cas(v = a.value, ((fn == null) ? v + x :
  42. fn.applyAsLong(v, x))))
  43. break;
  44. else if (n >= NCPU || cells != as)
  45. collide = false; // At max size or stale
  46. else if (!collide)
  47. collide = true;
  48. else if (cellsBusy == 0 && casCellsBusy()) {
  49. try {
  50. if (cells == as) { // Expand table unless stale
  51. Cell[] rs = new Cell[n << 1];
  52. for (int i = 0; i < n; ++i)
  53. rs[i] = as[i];
  54. cells = rs;
  55. }
  56. } finally {
  57. cellsBusy = 0;
  58. }
  59. collide = false;
  60. continue; // Retry with expanded table
  61. }
  62. h = advanceProbe(h);
  63. }
  64. // 以下判断是当cells为null时,并且cellsBusy为0,并且能够将cellsBusy自增为1时,也就是能够加锁时,进入判断内
  65. else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
  66. boolean init = false;
  67. try { // Initialize table
  68. if (cells == as) { // 默认初始化Cell数组大小为2
  69. Cell[] rs = new Cell[2];
  70. rs[h & 1] = new Cell(x);
  71. cells = rs;
  72. init = true;
  73. }
  74. } finally {
  75. cellsBusy = 0;
  76. }
  77. if (init)
  78. break;
  79. }
  80. // 以上条件都不满足,如果指定了operation方法,则使用operation进行操作,没有的话,则进行累加操作(这块是针对自定义operation的)
  81. else if (casBase(v = base, ((fn == null) ? v + x :
  82. fn.applyAsLong(v, x))))
  83. break; // Fall back on using base
  84. }
  85. }

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

  1. public LongAccumulator(LongBinaryOperator accumulatorFunction,
  2. long identity) {
  3. this.function = accumulatorFunction;
  4. base = this.identity = identity;
  5. }
  6.  
  7. // java8中的函数式接口可以自定义操作规则,累加、累乘等操作
  8. @FunctionalInterface
  9. public interface LongBinaryOperator {
  10.  
  11. /**
  12. * Applies this operator to the given operands.
  13. *
  14. * @param left the first operand
  15. * @param right the second operand
  16. * @return the operator result
  17. */
  18. long applyAsLong(long left, long right);
  19. }

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. 常用颜色的RGB分布

    RGB色彩模式是工业界的一种颜色标准,它通过对红(RED).绿(GREEN).蓝(BLUE)三种基本颜色的相互组合从而叠加出各种颜色.RGB色彩模式为每一个红.绿.蓝分类了0-255范围内的亮度值. ...

  2. 前端对base64编码的理解,原生js实现字符base64编码

    目录 常见对base64的认知(不完全正确) 多问一个为什么,base64到底是个啥? 按照我们的思路实现一下 到这里基本就实现了,结果跟原生的方法打印的是一样的 下一次 @( 对于前端工程师来说ba ...

  3. Tab选项卡点击 滑动效果js实现

    html部分代码: [html] css部分代码: *{ margin: ; padding:; list-style: none; font-size: 12px; } .notice{ width ...

  4. Codeforces Round #538 (Div. 2) (CF1114)

    Codeforces Round #538 (Div. 2) (CF1114)   今天昨天晚上的cf打的非常惨(仅代表淮中最低水平   先是一路缓慢地才A掉B,C,然后就开始杠D.于是写出了一个O( ...

  5. 基因id

    每个物种都有一个对应的Taxonomy ID: 9606 :人类 10090 :小鼠

  6. ASP.NET MVC中的捆绑和压缩技术

    概述 在众多Web性能优化的建议中有两条: 减少Http请求数量:大多数的浏览器同时处理向网站处理6个请求(参见下图),多余的请求会被浏览器要求排队等待,如果我们减少这些请求数,其他的请求等待的时间将 ...

  7. React笔记02——React中的组件

    一个网页可以被拆分成若干小部分,每个部分都可以称为组件,即组件是网页中的一部分.组件中还可以有多个组件. 上一节中的App.js就是一个组件(继承了React.Component类的类). 一个组件的 ...

  8. 【Vim编辑器】基本命令

    前言 工作中免不了会使用到vim编辑文档,总会觉得不好上手,遂从网上找到一篇说明文档整理如下,共勉. 原文地址: https://www.cnblogs.com/shiyanlou/archive/2 ...

  9. php chr()函数 语法

    php chr()函数 语法 作用:从指定的 ASCII 值返回字符.直线电机选型 语法:chr(ascii) 参数: 参数 描述 ascii  必须,指定ASCII值 说明:chr() 函数从指定的 ...

  10. JS中arguments对象

    与其他程序设计语言不同,ECMAScript 不会验证传递给函数的参数个数是否等于函数定义的参数个数. 开发者定义的函数都可以接受任意个数的参数而无需跟定义的函数相匹配(根据 Netscape 的文档 ...