死磕 java原子类之终结篇

  概览
  
  原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程上下文切换。
  
  原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分,将整个操作视作一个整体是原子性的核心特征。
  
  在java中提供了很多原子类,笔者在此主要把这些原子类分成四大类。
  
  atomic
  
  原子更新基本类型或引用类型
  
  如果是基本类型,则替换其值,如果是引用,则替换其引用地址,这些类主要有:
  
  (1)AtomicBoolean
  
  原子更新布尔类型,内部使用int类型的value存储1和0表示true和false,底层也是对int类型的原子操作。
  
  (2)AtomicInteger
  
  原子更新int类型。
  
  (3)AtomicLong
  
  原子更新long类型。
  
  (4)AtomicReference
  
  原子更新引用类型,通过泛型指定要操作的类。
  
  (5)AtomicMarkableReference
  
  原子更新引用类型,内部使用Pair承载引用对象及是否被更新过的标记,避免了ABA问题。
  
  (6)AtomicStampedReference
  
  原子更新引用类型,内部使用Pair承载引用对象及更新的邮戳,避免了ABA问题。
  
  这几个类的操作基本类似,底层都是调用Unsafe的compareAndSwapXxx()来实现,基本用法如下:
  
  private static void testAtomicReference() {
  
  AtomicInteger atomicInteger = new AtomicInteger(1);
  
  atomicInteger.incrementAndGet();
  
  atomicInteger.getAndIncrement();
  
  atomicInteger.compareAndSet(3, 666);
  
  System.out.println(atomicInteger.get());
  
  AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
  
  atomicStampedReference.compareAndSet(1, 2, 1, 3);
  
  atomicStampedReference.compareAndSet(2, 666, 3, 5);
  
  System.out.println(atomicStampedReference.getReference());
  
  System.out.println(atomicStampedReference.getStamp());
  
  }
  
  原子更新数组中的元素
  
  原子更新数组中的元素,可以更新数组中指定索引位置的元素,这些类主要有:
  
  (1)AtomicIntegerArray
  
  原子更新int数组中的元素。
  
  (2)AtomicLongArray
  
  原子更新long数组中的元素。
  
  (3)AtomicReferenceArray
  
  原子更新Object数组中的元素。
  
  这几个类的操作基本类似,更新元素时都要指定在数组中的索引位置,基本用法如下:
  
  private static void testAtomicReferenceArray() {
  
  AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);
  
  atomicIntegerArray.getAndIncrement(0);
  
  atomicIntegerArray.getAndAdd(1, 666);
  
  atomicIntegerArray.incrementAndGet(2);
  
  atomicIntegerArray.addAndGet(3, 666);
  
  atomicIntegerArray.compareAndSet(4, 0, 666);
  
  System.out.println(atomicIntegerArray.get(0));
  
  System.out.println(atomicIntegerArray.get(1));
  
  System.out.println(atomicIntegerArray.get(2));
  
  System.out.println(atomicIntegerArray.get(3));
  
  System.out.println(atomicIntegerArray.get(4));
  
  System.out.println(atomicIntegerArray.get(5));
  
  }
  
  原子更新对象中的字段
  
  原子更新对象中的字段,可以更新对象中指定字段名称的字段,这些类主要有:
  
  (1)AtomicIntegerFieldUpdater
  
  原子更新对象中的int类型字段。
  
  (2)AtomicLongFieldUpdater
  
  原子更新对象中的long类型字段。
  
  (3)AtomicReferenceFieldUpdater
  
  原子更新对象中的引用类型字段。
  
  这几个类的操作基本类似,都需要传入要更新的字段名称,基本用法如下:
  
  private static void testAtomicReferenceField() {
  
  AtomicReferenceFieldUpdater<User, String> updateName = AtomicReferenceFieldUpdater.newUpdater(User.class, String.class,"name");
  
  AtomicIntegerFieldUpdater<User> updateAge = AtomicIntegerFieldUpdater.newUpdater(www.hnxinhe.cn User.class, "age");
  
  User user = new User("tong ge", 21);
  
  updateName.compareAndSet( www.yunyouguoj.com/ user, "tong ge", "read source code");
  
  updateAge.compareAndSet(www.chenghgongs.com user, 21, 25);
  
  updateAge.incrementAndGet(user);
  
  System.out.println(user);
  
  }
  
  private static class User {
  
  volatile String name;
  
  volatile int age;
  
  public User(String name, int age) {
  
  this.name = name;
  
  this.age = age;
  
  }
  
  @Override
  
  public String toString() {
  
  return "name: " + name + "www.yunyouuyL.com , age: " + age;
  
  }
  
  }
  
  高性能原子类
  
  高性能原子类,是java8中增加的原子类,它们使用分段的思想,把不同的线程hash到不同的段上去更新,最后再把这些段的值相加得到最终的值,这些类主要有:
  
  (1)Striped64
  
  下面四个类的父类。
  
  (2)LongAccumulator
  
  long类型的聚合器,需要传入一个long类型的二元操作,可以用来计算各种聚合操作,包括加乘等。
  
  (3)LongAdder
  
  long类型的累加器,LongAccumulator的特例,只能用来计算加法,且从0开始计算。
  
  (4)DoubleAccumulator
  
  double类型的聚合器,需要传入一个double类型的二元操作,可以用来计算各种聚合操作,包括加乘等。
  
  (5)DoubleAdder
  
  double类型的累加器,DoubleAccumulator的特例,只能用来计算加法,且从0开始计算。
  
  这几个类的操作基本类似,其中DoubleAccumulator和DoubleAdder底层其实也是用long来实现的,基本用法如下:
  
  private static void testNewAtomic() {
  
  LongAdder longAdder = new LongAdder();
  
  longAdder.increment();
  
  longAdder.add(666);
  
  System.out.println(longAdder.sum(www.dell-case.com));
  
  LongAccumulator longAccumulator = new LongAccumulator((left, right)->left + right * 2, 666);
  
  longAccumulator.accumulate(www.baihuiyulegw.com );
  
  longAccumulator.accumulate(3);
  
  longAccumulator.accumulate(-4);
  
  System.out.println(longAccumulator.get());
  
  }
  
  问题
  
  关于原子类的问题,笔者整理了大概有以下这些:
  
  (1)Unsafe是什么?
  
  (3)Unsafe为什么是不安全的?
  
  (4)Unsafe的实例怎么获取?
  
  (5)Unsafe的CAS操作?
  
  (6)Unsafe的阻塞/唤醒操作?
  
  (7)Unsafe实例化一个类?
  
  (8)实例化类的六种方式?
  
  (9)原子操作是什么?
  
  (10)原子操作与数据库ACID中A的关系?
  
  (11)AtomicInteger怎么实现原子操作的?
  
  (12)AtomicInteger主要解决了什么问题?
  
  (13)AtomicInteger有哪些缺点?
  
  (14)ABA是什么?
  
  (15)ABA的危害?
  
  (16)ABA的解决方法?
  
  (17)AtomicStampedReference是怎么解决ABA的?
  
  (18)实际工作中遇到过ABA问题吗?
  
  (19)CPU的缓存架构是怎样的?
  
  (20)CPU的缓存行是什么?
  
  (21)内存屏障又是什么?
  
  (22)伪共享是什么原因导致的?
  
  (23)怎么避免伪共享?
  
  (24)消除伪共享在java中的应用?
  
  (25)LongAdder的实现方式?
  
  (26)LongAdder是怎么消除伪共享的?
  
  (27)LongAdder与AtomicLong的性能对比?
  
  (28)LongAdder中的cells数组是无限扩容的吗?
  
  关于原子类的问题差不多就这么多,都能回答上来吗?点击下面的链接可以直接到相应的章节查看:
  
  死磕 java魔法类之Unsafe解析
  
  死磕 java原子类之AtomicInteger源码分析
  
  死磕 java原子类之AtomicStampedReference源码分析
  
  杂谈 什么是伪共享(false sharing)?
  
  死磕 java原子类之LongAdder源码分析
  
  彩蛋
  
  原子类系列源码分析到此就结束了,虽然分析的类比较少,但是牵涉的内容非常多,特别是操作系统底层的知识,比如CPU指令、CPU缓存架构、内存屏障等。
  
  下一章,我们将进入“同步系列”,同步最常见的就是各种锁了,这里会着重分析java中的各种锁、各种同步器以及分布式锁相关的内容。

原文地址:https://www.cnblogs.com/qwangxiao/p/10860225.html

时间: 2024-10-04 22:30:40

死磕 java原子类之终结篇的相关文章

死磕 java原子类之终结篇(面试题)

概览 原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程上下文切换. 原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分,将整个操作视作一个整体是原子性的核心特征. 在java中提供了很多原子类,笔者在此主要把这些原子类分成四大类. 原子更新基本类型或引用类型 如果是基本类型,则替换其值,如果是引用,则替换其引用地址,这些类主要有: (1)AtomicBoolean 原子更新布尔类型,内部使用in

死磕 java同步系列之终结篇

简介 同步系列到此就结束了,本篇文章对同步系列做一个总结. 脑图 下面是关于同步系列的一份脑图,列举了主要的知识点和问题点,看过本系列文章的同学可以根据脑图自行回顾所学的内容,也可以作为面试前的准备. 如果有需要高清无码原图的同学,可以关注公众号"彤哥读源码",回复"sync"领取. 总结 所谓同步,就是保证多线程(包括多进程)对共享资源的读写能够安全有效的运行. 根据同步的运用场景的不同,实现同步的方式也是随之一起变化,但是总结下来,这些实现方式之间又有一些共通之

死磕 java集合之终结篇

概览 我们先来看一看java中所有集合的类关系图. 这里面的类太多了,请放大看,如果放大还看不清,请再放大看,如果还是看不清,请放弃. 我们下面主要分成五个部分来逐个击破. List List中的元素是有序的.可重复的,主要实现方式有动态数组和链表. java中提供的List的实现主要有ArrayList.LinkedList.CopyOnWriteArrayList,另外还有两个古老的类Vector和Stack. 关于List相关的问题主要有: (1)ArrayList和LinkedList有

死磕 java同步系列之AQS终篇(面试)

问题 (1)AQS的定位? (2)AQS的重要组成部分? (3)AQS运用的设计模式? (4)AQS的总体流程? 简介 AQS的全称是AbstractQueuedSynchronizer,它的定位是为Java中几乎所有的锁和同步器提供一个基础框架. 在之前的章节中,我们一起学习了ReentrantLock.ReentrantReadWriteLock.Semaphore.CountDownLatch的源码,今天我们一起来对AQS做个总结. 状态变量state AQS中定义了一个状态变量state

死磕 java同步系列之volatile解析

问题 (1)volatile是如何保证可见性的? (2)volatile是如何禁止重排序的? (3)volatile的实现原理? (4)volatile的缺陷? 简介 volatile可以说是Java虚拟机提供的最轻量级的同步机制了,但是它并不容易被正确地理解,以至于很多人不习惯使用它,遇到多线程问题一律使用synchronized或其它锁来解决. 了解volatile的语义对理解多线程的特性具有很重要的意义,所以彤哥专门写了一篇文章来解释volatile的语义到底是什么. 语义一:可见性 前面

死磕 java同步系列之redis分布式锁进化史

问题 (1)redis如何实现分布式锁? (2)redis分布式锁有哪些优点? (3)redis分布式锁有哪些缺点? (4)redis实现分布式锁有没有现成的轮子可以使用? 简介 Redis(全称:Remote Dictionary Server 远程字典服务)是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API. 本章我们将介绍如何基于redis实现分布式锁,并把其实现的进化史从头到尾讲明白,以便大家在面试的时候能讲清楚

死磕 Java 系列(一)&mdash;&mdash; 常用类(1) String 源码解析

写在前面 这是博主新开的一个 java 学习系列,听名字就可以看出来,在这一些系列中,我们学习的知识点不再是蜻蜓点水,而是深入底层,深入源码.由此,学习过程中我们要带着一股钻劲儿,对我们不懂的知识充满质疑,力求把我们学过的知识点都搞清楚,想明白. 一.引言 在 java 的世界里,存在一种特殊的类,它们的创建方式极为特别,不需要用到 new XXX(当然也可以用这种方式创建), 但是却大量出现在我们的代码中,那就是 String 类.作为日常中使用频率最高的类,它是那么普通,普通到我们从来都不会

【死磕Java并发】-----J.U.C之重入锁:ReentrantLock

此篇博客所有源码均来自JDK 1.8 ReentrantLock,可重入锁,是一种递归无阻塞的同步机制.它可以等同于synchronized的使用,但是ReentrantLock提供了比synchronized更强大.灵活的锁机制,可以减少死锁发生的概率. API介绍如下: 一个可重入的互斥锁定 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大.ReentrantLock 将由最近成功获得锁定,并且还没有释放该锁定的线程所拥

【死磕Java并发】-----J.U.C之AQS:CLH同步队列

此篇博客所有源码均来自JDK 1.8 在上篇博客[死磕Java并发]-–J.U.C之AQS:AQS简介中提到了AQS内部维护着一个FIFO队列,该队列就是CLH同步队列. CLH同步队列是一个FIFO双向队列,AQS依赖它来完成同步状态的管理,当前线程如果获取同步状态失败时,AQS则会将当前线程已经等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态. 在CLH同步队列中,一个节点表示一个线程