From a2fb6b467d1d05bd7520fa88377b8f1db86d2081 Mon Sep 17 00:00:00 2001 From: dbcxyxd <43689452+dbcxyxd@users.noreply.github.com> Date: Sat, 21 Dec 2019 22:31:43 +0800 Subject: [PATCH] 2019_12_21 --- week_02/51/AtomicInteger_51.md | 136 +++++++ week_02/51/Unsafe_51.md | 352 ++++++++++++++++++ .../\345\216\237\345\255\220\347\261\273.md" | 175 +++++++++ 3 files changed, 663 insertions(+) create mode 100644 week_02/51/AtomicInteger_51.md create mode 100644 week_02/51/Unsafe_51.md create mode 100644 "week_02/51/\345\216\237\345\255\220\347\261\273.md" diff --git a/week_02/51/AtomicInteger_51.md b/week_02/51/AtomicInteger_51.md new file mode 100644 index 0000000..2dc86f6 --- /dev/null +++ b/week_02/51/AtomicInteger_51.md @@ -0,0 +1,136 @@ +```java +//AtomicInteger的多步操作,都是利用Unsafe的compareAndSwapInt()方法来实现的 +//compareAndSwapInt 基于的是CPU 的 CAS指令来实现的。所以基于 CAS 的操作可认为是无阻塞的,一个线程的失败或挂起不会引起其它线程也失败或挂起。并且由于 CAS 操作是 CPU 原语,所以性能比较好 + +public class AtomicInteger extends Number implements java.io.Serializable { + private static final long serialVersionUID = 6214790243416807050L; + // setup to use Unsafe.compareAndSwapInt for updates + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long valueOffset; + + /**VM可以实现Java对象的布局,也就是在内存里Java对象的各个部分放在哪里,包括对象的实例字段和一些元数据之类。jdk内部使用的工具类sun.misc.Unsafe提供的方法objectFieldOffset()用于获取某个字段相对Java对象的“起始地址”的偏移量,可以使用这个偏移量调用getInt、getLong、getObject等方法来获取某个Java对象的某个字段。**/ + + static { + try { + //初始化valueOffset,这里的得到的是内存的偏移量。根据offset可以定位jvm中分配的内存地址。 + valueOffset = unsafe.objectFieldOffset + (AtomicInteger.class.getDeclaredField("value")); + } catch (Exception ex) { + throw new Error(ex); + } + } + private volatile int value; + /** * Creates a new AtomicInteger with the given initial value. * * @param initialValue the initial value */ + public AtomicInteger(int initialValue) { + value = initialValue; + } + /** * Creates a new AtomicInteger with initial value {@code 0}. */ + public AtomicInteger() { } + /** * Gets the current value. * * @return the current value */ + public final int get() { + return value; + } + /** * Sets to the given value. * * @param newValue the new value */ + public final void set(int newValue) { + value = newValue; + } + /** * Eventually sets to the given value. * * @param newValue the new value * @since 1.6 */ + public final void lazySet(int newValue) { + unsafe.putOrderedInt(this, valueOffset, newValue); + } + /** * Atomically sets to the given value and returns the old value. * * @param newValue the new value * @return the previous value */ + public final int getAndSet(int newValue) { + return unsafe.getAndSetInt(this, valueOffset, newValue); + } + /** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * * @param expect the expected value * @param update the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ + //利用unsafe实现CAS + public final boolean compareAndSet(int expect, int update) { + return unsafe.compareAndSwapInt(this, valueOffset, expect, update); + } + /** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * *

May fail * spuriously and does not provide ordering guarantees, so is * only rarely an appropriate alternative to {@code compareAndSet}. * * @param expect the expected value * @param update the new value * @return {@code true} if successful */ + public final boolean weakCompareAndSet(int expect, int update) { + return unsafe.compareAndSwapInt(this, valueOffset, expect, update); + } + /** * Atomically increments by one the current value. * * @return the previous value */ + public final int getAndIncrement() { + return unsafe.getAndAddInt(this, valueOffset, 1); + } + /** * Atomically decrements by one the current value. * * @return the previous value */ + public final int getAndDecrement() { + return unsafe.getAndAddInt(this, valueOffset, -1); + } + /** * Atomically adds the given value to the current value. * * @param delta the value to add * @return the previous value */ + public final int getAndAdd(int delta) { + return unsafe.getAndAddInt(this, valueOffset, delta); + } + /** * Atomically increments by one the current value. * * @return the updated value */ + + ///死循环,直到从内存中取到最新值,然后返回最新值+1 + public final int incrementAndGet() { + return unsafe.getAndAddInt(this, valueOffset, 1) + 1; + } + /** * Atomically decrements by one the current value. * * @return the updated value */ + public final int decrementAndGet() { + return unsafe.getAndAddInt(this, valueOffset, -1) - 1; + } + /** * Atomically adds the given value to the current value. * * @param delta the value to add * @return the updated value */ + public final int addAndGet(int delta) { + return unsafe.getAndAddInt(this, valueOffset, delta) + delta; + } + /** * Atomically updates the current value with the results of * applying the given function, returning the previous value. The * function should be side-effect-free, since it may be re-applied * when attempted updates fail due to contention among threads. * * @param updateFunction a side-effect-free function * @return the previous value * @since 1.8 */ + public final int getAndUpdate(IntUnaryOperator updateFunction) { + int prev, next; + do { + prev = get(); + next = updateFunction.applyAsInt(prev); + } while (!compareAndSet(prev, next)); + return prev; + } + /** * Atomically updates the current value with the results of * applying the given function, returning the updated value. The * function should be side-effect-free, since it may be re-applied * when attempted updates fail due to contention among threads. * * @param updateFunction a side-effect-free function * @return the updated value * @since 1.8 */ + public final int updateAndGet(IntUnaryOperator updateFunction) { + int prev, next; + do { + prev = get(); + next = updateFunction.applyAsInt(prev); + } while (!compareAndSet(prev, next)); + return next; + } + /** * Atomically updates the current value with the results of * applying the given function to the current and given values, * returning the previous value. The function should be * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function * is applied with the current value as its first argument, * and the given update as the second argument. * * @param x the update value * @param accumulatorFunction a side-effect-free function of two arguments * @return the previous value * @since 1.8 */ + public final int getAndAccumulate(int x,IntBinaryOperator accumulatorFunction) { + int prev, next; + do { + prev = get(); + next = accumulatorFunction.applyAsInt(prev, x); + } while (!compareAndSet(prev, next)); + return prev; + } + /** * Atomically updates the current value with the results of * applying the given function to the current and given values, * returning the updated value. The function should be * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function * is applied with the current value as its first argument, * and the given update as the second argument. * * @param x the update value * @param accumulatorFunction a side-effect-free function of two arguments * @return the updated value * @since 1.8 */ + public final int accumulateAndGet(int x,IntBinaryOperator accumulatorFunction) { + int prev, next; + do { + prev = get(); + next = accumulatorFunction.applyAsInt(prev, x); + } while (!compareAndSet(prev, next)); + return next; + } + /** * Returns the String representation of the current value. * @return the String representation of the current value */ + public String toString() { + return Integer.toString(get()); + } + /** * Returns the value of this {@code AtomicInteger} as an {@code int}. */ + public int intValue() { + return get(); + } + /** * Returns the value of this {@code AtomicInteger} as a {@code long} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions */ + public long longValue() { + return (long)get(); + } /** * Returns the value of this {@code AtomicInteger} as a {@code float} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions */ + public float floatValue() { + return (float)get(); + } + /** * Returns the value of this {@code AtomicInteger} as a {@code double} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions */ + public double doubleValue() { + return (double)get(); + } +} +``` \ No newline at end of file diff --git a/week_02/51/Unsafe_51.md b/week_02/51/Unsafe_51.md new file mode 100644 index 0000000..b7f2447 --- /dev/null +++ b/week_02/51/Unsafe_51.md @@ -0,0 +1,352 @@ +## 问题 + +(1)Unsafe是什么? + +(2)Unsafe只有CAS的功能吗? + +(3)Unsafe为什么是不安全的? + +(4)怎么使用Unsafe? + +## 简介 + +本章是java并发包专题的第一章,但是第一篇写的却不是java并发包中类,而是java中的魔法类sun.misc.Unsafe。 + +Unsafe为我们提供了访问底层的机制,这种机制仅供java核心类库使用,而不应该被普通用户使用。 + +但是,为了更好地了解java的生态体系,我们应该去学习它,去了解它,不求深入到底层的C/C++代码,但求能了解它的基本功能。 + +## 获取Unsafe的实例 + +查看Unsafe的源码我们会发现它提供了一个getUnsafe()的静态方法。 + +``` +@CallerSensitive + public static Unsafe getUnsafe() { + Class var0 = Reflection.getCallerClass(); + if (!VM.isSystemDomainLoader(var0.getClassLoader())) { + throw new SecurityException("Unsafe"); + } else { + return theUnsafe; + } + } +``` + +但是,如果直接调用这个方法会抛出一个SecurityException异常,这是因为Unsafe仅供java内部类使用,外部类不应该使用它。 + +那么,我们就没有方法了吗? + +当然不是,我们有反射啊!查看源码,我们发现它有一个属性叫theUnsafe,我们直接通过反射拿到它即可。 + +``` +private static final Unsafe theUnsafe; + +public class UnsafeTest { + public static void main(String[] args) throws NoSuchFieldException, +IllegalAccessException { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + Unsafe unsafe = (Unsafe) f.get(null); + } + } +``` + +## 使用Unsafe实例化一个类 + +假如我们有一个简单的类如下: + +``` +class User { + int age; + public User() { + this.age = 10; + } +} +``` + +如果我们通过构造方法实例化这个类,age属性将会返回10。 + +``` +User user1 = new User(); +// 打印10 +System.out.println(user1.age); +``` + +如果我们调用Unsafe来实例化呢? + +``` +User user2 = (User) unsafe.allocateInstance(User.class); +// 打印0 +System.out.println(user2.age); + +public native Object allocateInstance(Class var1) throws InstantiationException; +``` + +age将返回0,因为 `Unsafe.allocateInstance()`只会给对象分配内存,并不会调用构造方法,所以这里只会返回int类型的默认值0。 + +## 修改私有字段的值 + +使用Unsafe的putXXX()方法,我们可以修改任意私有字段的值。 + +``` +public class UnsafeTest { + public static void main(String[] args) throws Exception { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); +//设置Field对象的Accessible的访问标志位为Ture,就可以通过反射获取私有变量的值,在访问时会忽略访问修饰符的检查 + f.setAccessible(true); + Unsafe unsafe = (Unsafe) f.get(null); + User user = new User(); + Field age = user.getClass().getDeclaredField("age"); + unsafe.putInt(user, unsafe.objectFieldOffset(age), 20); + // 打印20 + System.out.println(user.getAge()); + } +} + +class User { + private int age; + public User() { + this.age = 10; + } + public int getAge() { + return age; + } + } + +//Unsafe的putXXX()方法 +//native 关键字告诉编译器(其实是JVM)调用的是该方法在外部定义,这里指的是C + public native int getInt(Object var1, long var2); + + public native void putInt(Object var1, long var2, int var4); + + public native Object getObject(Object var1, long var2); + + public native void putObject(Object var1, long var2, Object var4); + + public native boolean getBoolean(Object var1, long var2); + + public native void putBoolean(Object var1, long var2, boolean var4); + + public native byte getByte(Object var1, long var2); + + public native void putByte(Object var1, long var2, byte var4); + + public native short getShort(Object var1, long var2); + + public native void putShort(Object var1, long var2, short var4); + + public native char getChar(Object var1, long var2); + + public native void putChar(Object var1, long var2, char var4); + + public native long getLong(Object var1, long var2); + + public native void putLong(Object var1, long var2, long var4); + + public native float getFloat(Object var1, long var2); + + public native void putFloat(Object var1, long var2, float var4); + + public native double getDouble(Object var1, long var2); + + public native void putDouble(Object var1, long var2, double var4); +``` + +一旦我们通过反射调用得到字段age,我们就可以使用Unsafe将其值更改为任何其他int值。(当然,这里也可以通过反射直接修改) + +## 抛出checked异常 + +我们知道如果代码抛出了checked异常,要不就使用try...catch捕获它,要不就在方法签名上定义这个异常,但是,通过Unsafe我们可以抛出一个checked异常,同时却不用捕获或在方法签名上定义它。 + +``` +// 使用正常方式抛出IOException需要定义在方法签名上往外抛 +public static void readFile() throws IOException { + throw new IOException(); + } + // 使用Unsafe抛出异常不需要定义在方法签名上往外抛 + public static void readFileUnsafe() { + unsafe.throwException(new IOException()); + } +``` + +## 使用堆外内存 + +如果进程在运行过程中JVM上的内存不足了,会导致频繁的进行GC。理想情况下,我们可以考虑使用堆外内存,这是一块不受JVM管理的内存。 + +使用Unsafe的allocateMemory()我们可以直接在堆外分配内存,这可能非常有用,但我们要记住,这个内存不受JVM管理,因此我们要调用freeMemory()方法手动释放它。 + +假设我们要在堆外创建一个巨大的int数组,我们可以使用allocateMemory()方法来实现: + +``` +class OffHeapArray { +// 一个int等于4个字节 + private static final int INT = 4; + private long size; + private long address; + private static Unsafe unsafe; + static { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + unsafe = (Unsafe) f.get(null); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } +// 构造方法,分配内存 + public OffHeapArray(long size) { + this.size = size; + // 参数字节数 + address = unsafe.allocateMemory(size * INT); + } + // 获取指定索引处的元素 + public int get(long i) { + return unsafe.getInt(address + i * INT); + } + // 设置指定索引处的元素 + public void set(long i, int value) { + unsafe.putInt(address + i * INT, value); + } + // 元素个数 + public long size() { + return size; + } + // 释放堆外内存 + public void freeMemory() { + unsafe.freeMemory(address); + } +} +``` + +在构造方法中调用allocateMemory()分配内存,在使用完成后调用freeMemory()释放内存。 + +使用方式如下: + +OffHeapArray offHeapArray = new OffHeapArray(4); + +offHeapArray.set(0, 1); + +offHeapArray.set(1, 2); + +offHeapArray.set(2, 3); + +offHeapArray.set(3, 4); + +offHeapArray.set(2, 5); + +// 在索引2的位置重复放入元素 + +int sum = 0; + +for (int i = 0; i < offHeapArray.size(); i++) { + +​ sum += offHeapArray.get(i); + +} + +// 打印12 + +System.out.println(sum); + +offHeapArray.freeMemory(); + +最后,一定要记得调用freeMemory()将内存释放回操作系统。 + +## CompareAndSwap操作 + +JUC下面大量使用了CAS操作,它们的底层是调用的Unsafe的CompareAndSwapXXX()方法。这种方式广泛运用于无锁算法,与java中标准的悲观锁机制相比,它可以利用CAS处理器指令提供极大的加速。 + +比如,我们可以基于Unsafe的compareAndSwapInt()方法构建线程安全的计数器。 + +``` +class Counter { + private volatile int count = 0; + private static long offset; + private static Unsafe unsafe; + static { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + unsafe = (Unsafe) f.get(null); + offset = unsafe.objectFieldOffset(Counter.class.getDeclaredField("count")); } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } +public void increment() { + int before = count; + // 失败了就重试直到成功为止 + while (!unsafe.compareAndSwapInt(this, offset, before, before + 1)) { before = count; + } + } + public int getCount() { + return count; + } + } +``` + +我们定义了一个volatile的字段count,以便对它的修改所有线程都可见,并在类加载的时候获取count在类中的偏移地址。 + +在increment()方法中,我们通过调用Unsafe的compareAndSwapInt()方法来尝试更新之前获取到的count的值,如果它没有被其它线程更新过,则更新成功,否则不断重试直到成功为止。 + +我们可以通过使用多个线程来测试我们的代码: + +``` +Counter counter = new Counter(); +ExecutorService threadPool = Executors.newFixedThreadPool(100); +// 起100个线程,每个线程自增10000次 +IntStream.range(0, 100).forEach(i->threadPool.submit(()->IntStream.range(0, 10000) .forEach(j->counter.increment()))); +threadPool.shutdown(); +Thread.sleep(2000); +// 打印1000000 +System.out.println(counter.getCount()); +``` + +## park/unpark + +JVM在上下文切换的时候使用了Unsafe中的两个非常牛逼的方法park()和unpark()。 + +当一个线程正在等待某个操作时,JVM调用Unsafe的park()方法来阻塞此线程。 + +当阻塞中的线程需要再次运行时,JVM调用Unsafe的unpark()方法来唤醒此线程。 + +我们之前在分析java中的集合时看到了大量的LockSupport.park()/unpark(),它们底层都是调用的Unsafe的这两个方法。 + +## 总结 + +使用Unsafe几乎可以操作一切: + +(1)实例化一个类; + +(2)修改私有字段的值; + +(3)抛出checked异常; + +(4)使用堆外内存; + +(5)CAS操作; + +(6)阻塞/唤醒线程; + +## 彩蛋 + +论实例化一个类的方式? + +(1)通过构造方法实例化一个类; + +(2)通过Class实例化一个类; + +(3)通过反射实例化一个类; + +(4)通过克隆实例化一个类; + +(5)通过反序列化实例化一个类; + +(6)通过Unsafe实例化一个类; + +``` +public class InstantialTest { private static Unsafe unsafe; static { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); unsafe = (Unsafe) f.get(null); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } public static void main(String[] args) throws Exception { // 1. 构造方法 User user1 = new User(); // 2. Class,里面实际也是反射 User user2 = User.class.newInstance(); // 3. 反射 User user3 = User.class.getConstructor().newInstance(); // 4. 克隆 User user4 = (User) user1.clone(); // 5. 反序列化 User user5 = unserialize(user1); // 6. Unsafe User user6 = (User) unsafe.allocateInstance(User.class); System.out.println(user1.age); System.out.println(user2.age); System.out.println(user3.age); System.out.println(user4.age); System.out.println(user5.age); System.out.println(user6.age); } private static User unserialize(User user1) throws Exception { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D://object.txt")); oos.writeObject(user1); oos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D://object.txt")); // 反序列化 User user5 = (User) ois.readObject(); ois.close(); return user5; } static class User implements Cloneable, Serializable { private int age; public User() { this.age = 10; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }} +``` \ No newline at end of file diff --git "a/week_02/51/\345\216\237\345\255\220\347\261\273.md" "b/week_02/51/\345\216\237\345\255\220\347\261\273.md" new file mode 100644 index 0000000..f185199 --- /dev/null +++ "b/week_02/51/\345\216\237\345\255\220\347\261\273.md" @@ -0,0 +1,175 @@ +简介 + +原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程上下文切换。 + +原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分,将整个操作视作一个整体是原子性的核心特征。 + +在java中提供了很多原子类,笔者在此主要把这些原子类分成四大类。 + +![img](https://mmbiz.qpic.cn/mmbiz_png/C91PV9BDK3xBpxiaJoDxa91etlcDFiaOKvtPxcyuK94Nv46hSaRnTQGiccvRKOWOHRHTWKWgLfbiaMruqHuk7Zdc2Q/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) + +## 原子更新基本类型或引用类型 + +如果是基本类型,则替换其值,如果是引用,则替换其引用地址,这些类主要有: + +(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 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 updateName = AtomicReferenceFieldUpdater.newUpdater(User.class, String.class,"name"); AtomicIntegerFieldUpdater updateAge = AtomicIntegerFieldUpdater.newUpdater(User.class, "age"); User user = new User("tong ge", 21); updateName.compareAndSet(user, "tong ge", "read source code"); updateAge.compareAndSet(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 + ", 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()); LongAccumulator longAccumulator = new LongAccumulator((left, right)->left + right * 2, 666); longAccumulator.accumulate(1); 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数组是无限扩容的吗? \ No newline at end of file -- Gitee