jdk提供了原子性的数据类型。

  1. //主要有
  2. // AtomicInteger;
  3. // AtomicBoolean;
  4. // AtomicLong;
  5. AtomicInteger ai = new AtomicInteger();
  6. System.out.println(ai);
  7. System.out.println(ai.get());
  8. int i1 = ai.getAndIncrement();//获取并自增
  9. int i2 = ai.incrementAndGet();//自增后获取
  10. int i3 = ai.getAndDecrement();//获取后自减
  11. int i4 = ai.decrementAndGet();//自减后获取
  12. System.out.println(i1);
  13. System.out.println(i2);
  14. System.out.println(i3);
  15. System.out.println(i4);
  16. //输出
  17. //0
  18. //0
  19. //0
  20. //2
  21. //2
  22. //0
  23. AtomicInteger integer = new AtomicInteger(2);
  24. System.out.println("获取并添加:"+integer.getAndAdd(10));
  25. System.out.println("添加并获取:"+integer.addAndGet(10));
  26. integer.set(1);
  27. System.out.println(integer);
  28. System.out.println("获取并设置值:"+integer.getAndSet(2));
  29. System.out.println("添加并获取:"+integer.getAndUpdate(x->x+1));
  30. //输出:
  31. //获取并添加:2
  32. //添加并获取:22
  33. //1
  34. //获取并设置值:1
  35. //添加并获取:2

原子类使用CAS算法实现,效率比现场锁更高。

CAS算法


CAS,即 Compare And Swap(比较与交换),是一种无锁算法,基于硬件原语实现,能够在不使用锁的情况下实现多线程之间的变量同步。jdk中的java.util.concurrent.atomic包中的原子类就是通过CAS来实现了乐观锁。

CAS算法过程

算法涉及到三个操作数:

  • 需要读写的内存位置V
  • 需要进行比较的预期值A
  • 需要写入的新值U

CAS算法解析:
CAS具体执行时,当且仅当预期值A符合内存地址V中存储的值时,就用新值U替换掉旧值,并写入到内存地址V中。否则不做更新。
CAS算法的运行原理如下如所示:
原子类和CAS算法 - 图1
CAS会有如下三个方面的问题:
1.ABA问题,一个线程将内存值从A改为B,另一个线程又从B改回到A。
2.循环时间长开销大:CAS算法需要不断地自旋来读取最新的内存值,长时间读取不到就会造成不必要的CPU开销。

  1. 只能保证一个共享变量的原子操作(jdk的AtomicReference来保证应用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作,解决了这一问题)。

ABA问题图解:
原子类和CAS算法 - 图2
ABA问题解决方案:在变量前面添加版本号,每次变量更新的时候都将版本号加1,比如juc的原子包中的AtomicStampedReference类。

Java代码模拟CAS算法

这里使用synchronized线程锁关键字模拟硬件操作原语。

  1. package com.initit.j3_juc;
  2. import java.util.Random;
  3. /**
  4. * 模拟cas算法 CompareAndSwap 比较和交换算法
  5. *
  6. * 算法涉及到三个操作数:
  7. *
  8. * 需要读写的内存位置V
  9. * 需要进行比较的预期值A
  10. * 需要写入的新值U
  11. * CAS算法解析:
  12. *
  13. * CAS具体执行时,当且仅当预期值A符合内存地址V中存储的值时,就用新值U替换掉旧值,并写入到内存地址V中。否则不做更新。
  14. *
  15. * @author : liupeng
  16. * @email : 1029538990@qq.com
  17. * @date : 2020/5/23 11:02 下午
  18. */
  19. public class CASDemo {
  20. static final CompareAndSwap cas = new CompareAndSwap();
  21. public static void main(String[] args) {
  22. for (int i = 0; i < 10; i++) {
  23. new Thread(() -> {
  24. int expectedValue = cas.get();
  25. boolean b = cas.compareAndSet(expectedValue, new Random(100).nextInt());
  26. System.out.println(b);
  27. }).start();
  28. }
  29. }
  30. }
  31. class CompareAndSwap {
  32. private int value;
  33. //获取内存值
  34. public synchronized int get() {
  35. return value;
  36. }
  37. /**
  38. * 比较并设置值
  39. *
  40. * @param expectedValue 期望值
  41. * @param newValue 新值
  42. * @return boolean 设置成功返回true
  43. */
  44. public synchronized boolean compareAndSet(int expectedValue, int newValue) {
  45. return expectedValue == compareAndSwap(expectedValue, newValue);
  46. }
  47. /**
  48. * 比较和替换
  49. * 期望值跟新值一致时替换,否则不作任何操作
  50. *
  51. * @param expectedValue 期望值
  52. * @param newValue 新值
  53. * @return int 替换后的值
  54. */
  55. public synchronized int compareAndSwap(int expectedValue, int newValue) {
  56. int oldValue = value;
  57. if (oldValue == expectedValue) {
  58. this.value = newValue;
  59. }
  60. return oldValue;
  61. }
  62. }