• (十三) 共享模型之无锁【字段更新器、原子累加器、Unsafe】


    一、字段更新器(P175)

    J.U.C 并发包提供了:

    AtomicReferenceFieldUpdater // 域 字段

    AtomicIntegerFieldUpdater

    AtomicLongFieldUpdater

    利用字段更新器,可以针对对象的某个域(Field)进行原子操作,只能配合 volatile 修饰的字段使用,否则会出现异常。

    1. @Slf4j(topic = "c.Test40")
    2. public class Test40 {
    3. public static void main(String[] args) {
    4. Student stu = new Student();
    5. AtomicReferenceFieldUpdater updater =
    6. AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
    7. System.out.println(updater.compareAndSet(stu, null, "张三"));
    8. System.out.println(stu);
    9. }
    10. }
    11. class Student {
    12. volatile String name;
    13. @Override
    14. public String toString() {
    15. return "Student{" +
    16. "name='" + name + '\'' +
    17. '}';
    18. }
    19. }

    二、原子累加器

    1. 累加器性能比较

    1. public class Test41 {
    2. public static void main(String[] args) {
    3. for (int i = 0; i < 5; i++) {
    4. demo(
    5. () -> new AtomicLong(0),
    6. (adder) -> adder.getAndIncrement()
    7. );
    8. }
    9. for (int i = 0; i < 5; i++) {
    10. demo(
    11. () -> new LongAdder(),
    12. adder -> adder.increment()
    13. );
    14. }
    15. }
    16. /*
    17. () -> 结果 提供累加器对象
    18. (参数) -> 执行累加操作
    19. */
    20. private static void demo(Supplier adderSupplier, Consumer action) {
    21. T adder = adderSupplier.get();
    22. List ts = new ArrayList<>();
    23. // 4 个线程,每人累加 50 万
    24. for (int i = 0; i < 4; i++) {
    25. ts.add(new Thread(() -> {
    26. for (int j = 0; j < 500000; j++) {
    27. action.accept(adder);
    28. }
    29. }));
    30. }
    31. long start = System.nanoTime();
    32. ts.forEach(t -> t.start());
    33. ts.forEach(t -> {
    34. try {
    35. t.join();
    36. } catch (InterruptedException e) {
    37. e.printStackTrace();
    38. }
    39. });
    40. long end = System.nanoTime();
    41. System.out.println(adder + " cost:" + (end - start) / 1000_000);
    42. }
    43. }

     性能提升的原因很简单,就是在有竞争的时,设置多个累加单元,Thread - 0 累加 Cell[0],而 Thread - 1 累加 Cell [1] ......最后将结果汇总。这样它们在累加时操作的不同的 Cell 变量,因此减少了 CAS 重试失败,从而提高性能。

    2. * 源码之 LongAdder

    3. cas 锁(不要用于实现)

    1. @Slf4j(topic = "c.Test42")
    2. public class LockCas {
    3. // 0 没加锁
    4. // 1 加锁
    5. private AtomicInteger state = new AtomicInteger(0);
    6. public void lock() {
    7. while (true) {
    8. if (state.compareAndSet(0, 1)) {
    9. break;
    10. }
    11. }
    12. }
    13. public void unlock() {
    14. log.debug("unlock...");
    15. state.set(0);
    16. }
    17. public static void main(String[] args) {
    18. LockCas lock = new LockCas();
    19. new Thread(() -> {
    20. log.debug("begin...");
    21. lock.lock();
    22. try {
    23. log.debug("lock...");
    24. sleep(1);
    25. } finally {
    26. lock.unlock();
    27. }
    28. }).start();
    29. new Thread(() -> {
    30. log.debug("begin...");
    31. lock.lock();
    32. try {
    33. log.debug("lock...");
    34. } finally {
    35. lock.unlock();
    36. }
    37. }).start();
    38. }
    39. }

    4. * 原理之伪共享

    缓存与内存的速度比较

    (1)因为 CPU 与 内存的速度差异很大,需要靠预读数据至缓存来提升效率。
    (2)而缓存以缓存行为单位,每个缓存行对应着一块内存,一般是 64 byte 8 long
    (3)缓存的加入会造成数据副本的产生,即同一份数据会缓存在不同核心的缓存行中
    (4)CPU 要保证数据的一致性,如果某个 CPU 核心更改了数据,其它 CPU 核心对应的整个缓存行必须失效

    一些源码学习,下次再说吧。。。P177

    三、Unsafe

    1. 概述

    Unsafe 对象提供了非常底层的,操作内存、线程的方法(这些非常的危险),Unsafe 对象不能直接调用,只能通过反射获取。

     

    2. Unsafe CAS 操作

    1. public class TestUnsafe {
    2. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    3. Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    4. theUnsafe.setAccessible(true);
    5. Unsafe unsafe = (Unsafe) theUnsafe.get(null);
    6. System.out.println(unsafe);
    7. // 1. 获取域的偏移地址
    8. long idOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("id"));
    9. long nameOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("name"));
    10. Teacher t = new Teacher();
    11. // 2. 执行 cas 操作
    12. unsafe.compareAndSwapInt(t, idOffset, 0, 1);
    13. unsafe.compareAndSwapObject(t, nameOffset, null, "张三");
    14. // 3. 验证
    15. System.out.println(t);
    16. }
    17. }
    18. @Data
    19. class Teacher {
    20. volatile int id;
    21. volatile String name;
    22. }

    3. 模拟实现原子整数

    1. @Slf4j(topic = "c.Test42")
    2. public class Test42 {
    3. public static void main(String[] args) {
    4. Account.demo(new MyAtomicInteger(10000));
    5. }
    6. }
    7. class MyAtomicInteger implements Account {
    8. private volatile int value;
    9. private static final long valueOffset;
    10. private static final Unsafe UNSAFE;
    11. static {
    12. UNSAFE = UnsafeAccessor.getUnsafe();// 自定义的方法获取unsafe
    13. try {
    14. valueOffset = UNSAFE.objectFieldOffset(MyAtomicInteger.class.getDeclaredField("value"));
    15. } catch (NoSuchFieldException e) {
    16. e.printStackTrace();
    17. throw new RuntimeException(e);
    18. }
    19. }
    20. public int getValue() {
    21. return value;
    22. }
    23. public void decrement(int amount) {
    24. while(true) {
    25. int prev = this.value;
    26. int next = prev - amount;
    27. if (UNSAFE.compareAndSwapInt(this, valueOffset, prev, next)) {
    28. break;
    29. }
    30. }
    31. }
    32. public MyAtomicInteger(int value) {
    33. this.value = value;
    34. }
    35. @Override
    36. public Integer getBalance() {
    37. return getValue();
    38. }
    39. @Override
    40. public void withdraw(Integer amount) {
    41. decrement(amount);
    42. }
    43. }

  • 相关阅读:
    长安三万里,安全无边界
    【解决】运行vue项目,启动报错 in ./node_modules/@intlify/core-base/dist/core-base.cjs
    项目可交付成果的质量管理该怎么做?
    Django使用正则表达式
    设计模式(结构型设计模式——桥接模式)
    iOS多界面传值
    uboot 添加命令
    红外海洋目标检测实践,基于目标检测模型识别红外海洋目标
    一文解读 NFT 零版税
    SpringCloud的新闻资讯项目01--- 环境搭建、SpringCloud微服务(注册发现、服务调用、网关
  • 原文地址:https://blog.csdn.net/yirenyuan/article/details/128187641