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 一定会创建新的对象分配新的内存空间;

相关文章
|
25天前
|
缓存 安全 Java
Java并发编程进阶:深入理解Java内存模型
Java并发编程进阶:深入理解Java内存模型
33 0
|
9天前
|
缓存 Java 编译器
JAVA并发编程volatile核心原理
volatile是轻量级的并发解决方案,volatile修饰的变量,在多线程并发读写场景下,可以保证变量的可见性和有序性,具体是如何实现可见性和有序性。以及volatile缺点是什么?
|
22天前
|
设计模式 安全 Java
Java 单例模式,背后有着何种不为人知的秘密?开启探索之旅,寻找答案!
【8月更文挑战第30天】单例模式确保一个类只有一个实例并提供全局访问点,适用于需全局共享的宝贵资源如数据库连接池、日志记录器等。Java中有多种单例模式实现,包括饿汉式、懒汉式、同步方法和双重检查锁定。饿汉式在类加载时创建实例,懒汉式则在首次调用时创建,后者在多线程环境下需使用同步机制保证线程安全。单例模式有助于提高代码的可维护性和扩展性,应根据需求选择合适实现方式。
29 1
|
25天前
|
SQL 设计模式 安全
Java编程中的单例模式深入解析
【8月更文挑战第27天】本文旨在探索Java中实现单例模式的多种方式,并分析其优缺点。我们将通过代码示例,展示如何在不同的场景下选择最合适的单例模式实现方法,以及如何避免常见的陷阱。
|
29天前
|
存储 Java
Java 中 ConcurrentHashMap 的并发级别
【8月更文挑战第22天】
34 5
|
29天前
|
存储 算法 Java
Java 中的同步集合和并发集合
【8月更文挑战第22天】
21 5
|
28天前
|
缓存 Java 调度
【Java 并发秘籍】线程池大作战:揭秘 JDK 中的线程池家族!
【8月更文挑战第24天】Java的并发库提供多种线程池以应对不同的多线程编程需求。本文通过实例介绍了四种主要线程池:固定大小线程池、可缓存线程池、单一线程线程池及定时任务线程池。固定大小线程池通过预设线程数管理任务队列;可缓存线程池能根据需要动态调整线程数量;单一线程线程池确保任务顺序执行;定时任务线程池支持周期性或延时任务调度。了解并正确选用这些线程池有助于提高程序效率和资源利用率。
34 2
|
29天前
|
安全 Java 编译器
Java 中的 volatile 变量
【8月更文挑战第22天】
21 4
|
20天前
|
设计模式 安全 Java
Java编程中的单例模式深度解析
【8月更文挑战第31天】 单例模式,作为设计模式中的经典之一,在Java编程实践中扮演着重要的角色。本文将通过简洁易懂的语言,逐步引导读者理解单例模式的本质、实现方法及其在实际应用中的重要性。从基础概念出发,到代码示例,再到高级应用,我们将一起探索这一模式如何优雅地解决资源共享和性能优化的问题。
|
21天前
|
设计模式 安全 Java
Java中的单例模式:理解与实践
【8月更文挑战第31天】在软件设计中,单例模式是一种常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点。本文将深入探讨Java中实现单例模式的不同方法,包括懒汉式、饿汉式、双重校验锁以及静态内部类等方法。每种方法都有其适用场景和潜在问题,我们将通过代码示例来展示如何根据具体需求选择合适的实现方式。