Java并发JUC(java.util.concurrent)Volatile\单例模式\CAS\原子引用

简介: Java并发JUC(java.util.concurrent)Volatile\单例模式\CAS\原子引用

在这里插入图片描述

👨🏻‍🎓博主介绍:大家好,我是芝士味的椒盐,一名在校大学生,热爱分享知识,很高兴在这里认识大家🌟
🌈擅长领域:Java、大数据、运维、电子
🙏🏻如果本文章各位小伙伴们有帮助的话,🍭关注+👍🏻点赞+🗣评论+📦收藏,相应的有空了我也会回访,互助!!!
🤝另本人水平有限,旨在创作简单易懂的文章,在文章描述时如有错,恳请各位大佬指正,在此感谢!!!

@[TOC]

Volatile

  • 保证可见性

    package icu.lookyousmileface.volatilecode;
    
    import java.util.concurrent.Executors;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author starrysky
     * @title: VolatileUse
     * @projectName Juc_Pro
     * @description: volatile原子性测试
     * @date 2021/1/307:52 上午
     */
    public class VolatileShow {
        /**
         * // 不加 volatile 程序就会死循环!
         * // 加 volatile 可以保证可见性
         */
        private  volatile static int num = 0;
    
        public static void main(String[] args) {
    
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    5,
                    Runtime.getRuntime().availableProcessors(),
                    5,
                    TimeUnit.SECONDS,
                    new LinkedBlockingQueue<>(3),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.DiscardOldestPolicy()
            );
            //线程1对内存中num=1的变化不知道
            threadPoolExecutor.execute(() -> {
                while (num == 0) {
    
                }
            });
    
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            num = 1;
    
            System.out.println("num:"+num);
    
            threadPoolExecutor.shutdown();
    
        }
    }
  • 不保证原子性

    原子性 : 不可分割

    线程A在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败。

    /**
     * @author starrysky
     * @title: VolatileNotAtomic
     * @projectName Juc_Pro
     * @description: Volatile不保证原子性
     * @date 2021/1/3011:32 上午
     */
    public class VolatileNotAtomic {
    
        private volatile static int num = 0;
    
        public  static void add(){
            num++;
        }
    
        public static void main(String[] args) {
    
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    5,
                    Runtime.getRuntime().availableProcessors(),
                    5,
                    TimeUnit.SECONDS,
                    new LinkedBlockingQueue<>(3),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.CallerRunsPolicy()
            );
    
            try {
                for (int i = 1; i <= 20; i++) {
                    threadPoolExecutor.execute(() -> {
                        for (int j = 1; j <= 1000; j++) {
                            add();
                        }
                    });
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                threadPoolExecutor.shutdown();
            }
            Thread.currentThread().getThreadGroup().list();
    
            System.out.println("sum:" + num);
    
        }
    }
    • 为了保证原子性,不实用sync、lock,可以使用atomic包下的原子类

      /**
       * @author starrysky
       * @title: AtomicUse
       * @projectName Juc_Pro
       * @description: 原子类使用
       * @date 2021/1/3011:51 上午
       */
      public class AtomicUse {
      
          private volatile static AtomicInteger number = new AtomicInteger();
      
          public static void add(){
              number.getAndIncrement();
          }
      
          public static void main(String[] args) {
      
              ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                      5,
                      Runtime.getRuntime().availableProcessors(),
                      5,
                      TimeUnit.SECONDS,
                      new LinkedBlockingQueue<>(3),
                      Executors.defaultThreadFactory(),
                      new ThreadPoolExecutor.CallerRunsPolicy()
      
              );
      
              try {
                  for (int i = 1; i <= 20; i++) {
                      threadPoolExecutor.execute(()->{
                          for (int j = 1; j <= 1000; j++) {
                              add();
                          }
                      });
                  }
      
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  threadPoolExecutor.shutdown();
              }
              /**
               * 让出主线程的CPU时间片,让其他线程使用
               */
              while (Thread.activeCount()>2){
                  System.out.println(Thread.currentThread().getName());
                  Thread.yield();
              }
              System.out.println("result:"+number);
          }
      }
  • 禁止指令重排

    什么是 指令重排:你写的程序,计算机并不是按照你写的那样去执行的。

    源代码-->编译器优化的重排--> 指令并行也可能会重排--> 内存系统也会重排---> 执行

    处理器在进行指令重排的时候,考虑:数据之间的依赖性!

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oVkWRf6K-1644224594265)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/acc5d510-aa44-4c78-bcba-854975117af7/Untitled.png)]

单例模式

  • 饿汉 单例模式

    /**
     * @author starrysky
     * @title: Hungry
     * @projectName Juc_Pro
     * @description: 饿汉 单例模式
     * @date 2021/1/304:47 下午
     */
    public class Hungry {
        private Hungry(){
    
        }
        private final static Hungry HUNGRY = new Hungry();
    
        public static Hungry getInstance(){
            return HUNGRY;
        }
    }
  • 懒汉 单例模式

    /**
     * @author starrysky
     * @title: LazyMan
     * @projectName Juc_Pro
     * @description: 懒汉单例模式
     * @date 2021/1/304:52 下午
     */
    public class LazyMan {
    
        private  volatile static LazyMan lazyMan;
        private static boolean lockSkey = false;
    
        private LazyMan() {
            synchronized (LazyMan.class){
                if (lockSkey == false){
                    lockSkey = true;
                }else {
                    throw new RuntimeException("不要妄图使用反射破坏!");
                }
            }
        }
        // 双重检测锁模式的 懒汉式单例 DCL懒汉式
        public static LazyMan getInstance() {
            if (lazyMan == null) {
                synchronized (LazyMan.class){
                    if (lazyMan==null){
                        lazyMan = new LazyMan();
                    }
                }
            }
            return lazyMan;
        }
    
    }
  • 静态内部类

    /**
     * @author starrysky
     * @title: StaticClass
     * @projectName Juc_Pro
     * @description: 静态内部类
     * @date 2021/1/307:11 下午
     */
    public class StaticClass {
    
        private StaticClass(){
    
        }
        private static class InnerClass{
            private static final Holder HOLDER = new Holder();
        }
    
        private static Holder getInstenac(){
           return InnerClass.HOLDER;
        }
    }

CAS

  • 修内功,操作系统,计算机网络原理。
  • 原子类

    /**
     * @author starrysky
     * @title: CasUse
     * @projectName Juc_Pro
     * @description: Cas
     * @date 2021/1/307:54 下午
     */
    public class CasUse {
        public static void main(String[] args) {
            AtomicInteger atomicInteger = new AtomicInteger(12);
                    //期望值,更新值
            System.out.println(atomicInteger.compareAndSet(12, 66));
            System.out.println(atomicInteger.get());
            System.out.println(atomicInteger.compareAndSet(66, 99));
            System.out.println(atomicInteger.get());
            
        }
    }
  • Unsafe类

    import java.util.function.IntUnaryOperator;
    import java.util.function.IntBinaryOperator;
    import sun.misc.Unsafe;
    
    /**
     * An {@code int} value that may be updated atomically.  See the
     * {@link java.util.concurrent.atomic} package specification for
     * description of the properties of atomic variables. An
     * {@code AtomicInteger} is used in applications such as atomically
     * incremented counters, and cannot be used as a replacement for an
     * {@link java.lang.Integer}. However, this class does extend
     * {@code Number} to allow uniform access by tools and utilities that
     * deal with numerically-based classes.
     *
     * @since 1.5
     * @author Doug Lea
    */
    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;
    
        static {
            try {
                valueOffset = unsafe.objectFieldOffset
                    (AtomicInteger.class.getDeclaredField("value"));
            } catch (Exception ex) { throw new Error(ex); }
        }
    • ⚠️ Java无法操作内存,c++可以,Java操作C++,C++再操作内存
    public final int getAndAddInt(Object o, long offset, int delta) {
            int v;
            do {
                    //内存中的值
                v = getIntVolatile(o, offset);
                    //自旋锁,CAS : 比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!
            } while (!compareAndSwapInt(o, offset, v, v + delta));
            return v;
        }
    • 缺点:

      1、 循环会耗时

      2、一次性只能保证一个共享变量的原子性

      3、ABA问题

  • ABA问题(立马换太子)

    /**
     * @author starrysky
     * @title: CasABAProblem
     * @projectName Juc_Pro
     * @description: ABA问题, 立马换太子问题
     * @date 2021/1/308:03 下午
     */
    public class CasABAProblem {
        public static void main(String[] args) {
            AtomicInteger atomicInteger = new AtomicInteger(20);
    
            //====================捣乱换太子=============
            atomicInteger.compareAndSet(20,66);
            atomicInteger.compareAndSet(66,20);
            //==================== 不知情的线程===========
            atomicInteger.compareAndSet(20,99);
            System.out.println(atomicInteger.get());
        }
    }

原子引用

  • 解决ABA 问题,引入原子引用! 对应的思想:乐观锁!
  • 带版本号 的原子操作!
  • 试验代码

    package icu.lookyousmileface.cas;
    
    import java.util.concurrent.Executors;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicStampedReference;
    
    /**
     * @author starrysky
     * @title: ReferenceUse
     * @projectName Juc_Pro
     * @description: AtomicStampedReference
     * @date 2021/1/308:17 下午
     */
    public class ReferenceUse {
        public static void main(String[] args) {
            AtomicStampedReference<Integer> atomicRefe = new AtomicStampedReference<>(1, 1);
    
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    5,
                    Runtime.getRuntime().availableProcessors(),
                    5,
                    TimeUnit.SECONDS,
                    new LinkedBlockingQueue<>(3),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.CallerRunsPolicy()
            );
    
            try {
                threadPoolExecutor.execute(()->{
                    int stamp = atomicRefe.getStamp();
                    System.out.println(Thread.currentThread().getName()+"=>"+stamp);
    
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                    atomicRefe.compareAndSet(1,2,atomicRefe.getStamp(),atomicRefe.getStamp()+1);
                    System.out.println(Thread.currentThread().getName()+"=>"+atomicRefe.getStamp());
    
                    System.out.println(atomicRefe.compareAndSet(2, 1, atomicRefe.getStamp(), atomicRefe.getStamp() + 1));
                    System.out.println(Thread.currentThread().getName()+"=>"+atomicRefe.getStamp());
                });
                /**
                 * 和乐观锁的原理一样
                 */
                threadPoolExecutor.execute(()->{
                    int stamp = atomicRefe.getStamp();
                    System.out.println(Thread.currentThread().getName()+"=>"+stamp);
    
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                    System.out.println(atomicRefe.compareAndSet(1, 2, stamp, stamp + 1));
    
                    System.out.println(Thread.currentThread().getName()+"=>"+atomicRefe.getStamp());
                });
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
            threadPoolExecutor.shutdown();
            }
        }
    }

    ⚠️ Tips:Integer 使用了对象缓存机制,默认范围是 -128 ~ 127 ,推荐使用静态工厂方法 valueOf 获取对象实例,而不是 new,因为 valueOf 使用缓存,而 new 一定会创建新的对象分配新的内存空间;

相关文章
|
5天前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
5天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
5天前
|
缓存 安全 Java
Java volatile关键字:你真的懂了吗?
`volatile` 是 Java 中的轻量级同步机制,主要用于保证多线程环境下共享变量的可见性和防止指令重排。它确保一个线程对 `volatile` 变量的修改能立即被其他线程看到,但不能保证原子性。典型应用场景包括状态标记、双重检查锁定和安全发布对象等。`volatile` 适用于布尔型、字节型等简单类型及引用类型,不适用于 `long` 和 `double` 类型。与 `synchronized` 不同,`volatile` 不提供互斥性,因此在需要互斥的场景下不能替代 `synchronized`。
2068 3
|
5天前
|
安全 算法 Java
Java CAS原理和应用场景大揭秘:你掌握了吗?
CAS(Compare and Swap)是一种乐观锁机制,通过硬件指令实现原子操作,确保多线程环境下对共享变量的安全访问。它避免了传统互斥锁的性能开销和线程阻塞问题。CAS操作包含三个步骤:获取期望值、比较当前值与期望值是否相等、若相等则更新为新值。CAS广泛应用于高并发场景,如数据库事务、分布式锁、无锁数据结构等,但需注意ABA问题。Java中常用`java.util.concurrent.atomic`包下的类支持CAS操作。
25 2
|
2月前
|
存储 消息中间件 安全
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
【10月更文挑战第9天】本文介绍了如何利用JUC组件实现Java服务与硬件通过MQTT的同步通信(RRPC)。通过模拟MQTT通信流程,使用`LinkedBlockingQueue`作为消息队列,详细讲解了消息发送、接收及响应的同步处理机制,包括任务超时处理和内存泄漏的预防措施。文中还提供了具体的类设计和方法实现,帮助理解同步通信的内部工作原理。
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
|
2月前
|
SQL 缓存 安全
[Java]volatile关键字
本文介绍了Java中volatile关键字的原理与应用,涵盖JMM规范、并发编程的三大特性(可见性、原子性、有序性),并通过示例详细解析了volatile如何实现可见性和有序性,以及如何结合synchronized、Lock和AtomicInteger确保原子性,最后讨论了volatile在单例模式中的经典应用。
61 0
|
安全 Java
Java常用的单例模式
Java常用的单例模式
126 0
|
安全 Java
Java常用的单例模式
版权声明:欢迎转载,请注明沉默王二原创。 https://blog.csdn.net/qing_gee/article/details/46608497 Java的单例single经常用到,但是使用枚举enum最佳。
946 0
|
3天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
5天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。