• JUC常用


    JUC常用辅助类

    1、CountDownLatch(减法)

    允许一个或者多个线程去等待其他线程完成操作。CountDownLatch接收一个int型参数,表示要等待的工作线程的个数。

    等于0主线程才往下执行

    CountDownLatch countDownLatch = new CountDownLatch(6);
    //-1
    countDownLatch.countDown();
    //等待计数器归零才会向下执行
     countDownLatch.await();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public class CountDownLatchDemo {
        public static void main(String[] args) throws InterruptedException {
            //总数是6
            CountDownLatch countDownLatch = new CountDownLatch(6);
            for (int i = 0; i < 6; i++) {
                new Thread(()->{
                    System.out.println(Thread.currentThread().getName()+"go out");
                    countDownLatch.countDown();
                },String.valueOf(i)).start();
            }
            //等待计数器归零才会向下执行
            countDownLatch.await();
            countDownLatch.countDown();//-1
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2、CyclicBarrier(加法)

    有若干个线程,比如说有五个线程,需要它们都到达了某一个点之后才能开始一起执行,也就是说假如其中只有四个线程到达了这个点,还差一个线程没到达,此时这四个线程都会进入等待状态,直到第五个线程也到达了这个点之后,这五个线程才开始一起进行执行状态

    等待的线程达到7个,才一起被唤醒最后一个执行的线程执行召唤神龙

            CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
                System.out.println("召唤神龙成功");
            });
    		
    		cyclicBarrier.await();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    
    public class CyclicBarrierDemo {
        public static void main(String[] args) {
            //集齐七颗龙珠,召唤神龙
            //召唤龙珠
            CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
                System.out.println("召唤神龙成功");
            });
            for (int i = 0; i < 7; i++) {
                final int temp = i;
                //lambda不能直接拿到for循环中的i
                new Thread(()->{
                    System.out.println(Thread.currentThread().getName()+"收集"+temp);
                    try {
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    3、Semaphore(抢占)

    Semaphore内部并没有真正保存P,而是只保存了P的个数。其次,Semaphore直接复用了AbstractQueueSyncrhonizer框架的共享模式锁,其acquire和release操作直接调用共享模式的AQS加锁和AQS解锁,没有增加其他逻辑,只不过在加锁和解锁的过程中,是把P的个数存入AQS原子整数。

    线程中限制次数等待抢占,满了就等待其他线程执行完毕

    Semaphore semaphore = new Semaphore(3);
    semaphore.acquire();//获得,如果满了,会等待被释放为止
    semaphore.release();//释放
    
    • 1
    • 2
    • 3
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    
    public class SemaphoreDemo {
        public static void main(String[] args) {
            Semaphore semaphore = new Semaphore(3);
            for (int i = 1; i <= 6; i++) {
                new Thread(()->{
                    //acquire()
                    try {
                        semaphore.acquire();//获得,如果满了,会等待被释放为止
                        System.out.println(Thread.currentThread().getName()+"抢到车位");
                        TimeUnit.SECONDS.sleep(2);
                        System.out.println(Thread.currentThread().getName()+"离开车位");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        semaphore.release();//释放
                    }
                },String.valueOf(i)).start();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    lock

    1、ReentrantLock

    再重写的run中进行锁操作

                ReentrantLock reentrantLock = new ReentrantLock();
                Condition condition = reentrantLock.newCondition();
    //            睡眠  不建议使用 Thread.sleep(10);
                TimeUnit.SECONDS.sleep(10);
    //            等待
                condition.await();
    //            唤醒
                condition.signal();
    //            唤醒全部
                condition.signalAll();
    //            锁
                reentrantLock.lock();
    //            释放
                reentrantLock.unlock();            ReentrantLock reentrantLock = new ReentrantLock();
                Condition condition = reentrantLock.newCondition();
    //            等待
                condition.await();
    //            唤醒
                condition.signal();
    //            唤醒全部
                condition.signalAll();
    //            锁
                reentrantLock.lock();
    //            释放
                reentrantLock.unlock();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    2、ReentrantReadWriteLock读写锁

    区别查看 读写锁使用区别

                ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    //            读锁
                ReentrantReadWriteLock.ReadLock readLock1 = readWriteLock.readLock();
    //            写锁
                ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3、Atomic 原子类

    解决并发的线程安全问题有两种方式:
    1、等待唤醒机制
    如果抢不到锁,就将线程挂起,当锁释放的时候,然后将其唤醒重新抢锁。
    2、自旋CAS
    自旋就是设置循环CAS抢锁的意思,当CAS成功的时候才会退出循环
    在这里插入图片描述
    Atomic原子类就是利用自旋CAS来保证线程安全的。

    1、基本常用方法

    getAndIncrement() // 原子化 i++
    getAndDecrement() // 原子化的 i--
    incrementAndGet() // 原子化的 ++i
    decrementAndGet() // 原子化的 --i
    // 当前值 +=delta,返回 += 前的值
    getAndAdd(delta) 
    // 当前值 +=delta,返回 += 后的值
    addAndGet(delta)
    //CAS 操作,返回是否成功
    compareAndSet(expect, update)
    // 以下四个方法
    // 新值可以通过传入 func 函数来计算
    getAndUpdate(func)
    updateAndGet(func)
    getAndAccumulate(x,func)
    accumulateAndGet(x,func)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2、对象引用类型

    User user = new User("jaychou",24);
    AtomicReference<User> userAtomicReference = new AtomicReference<>(user);
    
    while (true){
        User updateUser = new User("jay",22);
        boolean flag = userAtomicReference.compareAndSet(userAtomicReference.get(),updateUser);
    
        if (flag){
            System.out.println(userAtomicReference.get());
            break;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    AtomicStampedReference

    AtomicReference有一个缺点,不能解决ABA问题。
    ABA问题就是多个线程前后修改值,导致线程CAS前后值没有变化,但是中间却发生了修改。

    AtomicStampedReference通过引入时间戳来解决了ABA问题。每次要更新值的时候,需要额外传入oldStamp和newStamp。将对象和stamp包装成了一个Pair对象。

    User user = new User("jaychou",24);
    AtomicStampedReference<User> userAtomicStampedReference = new AtomicStampedReference<>(user,1);
    
    while (true){
        User user1 = new User("jay",222);
        int oldStamp1 = userAtomicStampedReference.getStamp();
        int[] stamp = new int[1];
        User oldUser = userAtomicStampedReference.get(stamp);
        boolean flag = userAtomicStampedReference.compareAndSet(oldUser,user1,stamp[0],stamp[0]+1);
        if (flag){
            break;
        }
    }
    
    int[] s = new int[1];
    System.out.println(userAtomicStampedReference.get(s));
    System.out.println(s[0]);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    AtomicMarkableReference是无法解决ABA问题的,因为boolean变量的mark是有很大可能重合的,还是会导致更新成功。

    3、Atomic数组 Atomic数组主要有AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray.

    AtomicIntegerArray integerArray = new AtomicIntegerArray(10);//10为数组长度
    while (true){
        boolean flag = integerArray.compareAndSet(0,integerArray.get(0),2);
        if (flag){
            System.out.println(integerArray.get(0)+"---"+flag);
            break;
        }
    }
    
    AtomicReferenceArray<User> referenceArray = new AtomicReferenceArray<>(10);
    while (true){
        boolean flag2 = referenceArray.compareAndSet(0,referenceArray.get(0),new User("jaychou",22));
        if (flag2){
            System.out.println(referenceArray.get(0)+"---"+flag2);
            break;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    4、对象属性原子更新器 有三类:AtomicIntegerFieldUpdater(修改对象中的)、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater

    
    User user = new User("jaychou",22);
    
    //初始化参数分别为:对应对应的类,属性对应的类,属性的名字
    AtomicReferenceFieldUpdater fieldUpdater = AtomicReferenceFieldUpdater.newUpdater(User.class,String.class,"name");
    //初始化参数分别为:对应对应的类,属性的名字。属性对应的类可以忽略,因为类名中已经记录了
    AtomicIntegerFieldUpdater integerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(User.class,"age");
    
    fieldUpdater.compareAndSet(user,user.name,"666");
    integerFieldUpdater.compareAndSet(user,user.age,1000);
    
    System.out.println(user);
    
    
    
    class User{
        int age;
        volatile String name;
    
        public User(String name,int age){
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    Java中提供了几类原子操作类,通过自旋+CAS来解决线程不安全问题。
    1、AtomicLong、AtomicInteger、AtomicBoolean
    这些类实现了++,–,+delta的原子操作。
    2、AtomicReference
    AtomicReference的作用是引用类型的原子修改,保证引用的修改不会出现线程问题。
    并且通过AtomicStampedReference解决了ABA问题。
    3、AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
    这个就是数组类型,和单独的对象操作基本一致,只不过在设置的时候需要填入下标罢了。
    4、AtomicIntegerFieldUpdater、AtomicReferenceFieldUpdater、AtomicLongFieldUpdater
    AtomicReference是修改引用,而AtomicReferenceFieldUpdater这三个类是用来修改实例对象中的属性的值的。
    AtomicIntegerFieldUpdater、AtomicLongFieldUpdater是AtomicReferenceFieldUpdater的一个特殊例子,是用来专门分别修改int和long属性的变量的。

    出以上被修改变量必须用volatile修饰

    问题

    1、Sychronized和lock的区别

    1.Sychronized 内置的java关键字,Lock锁是一个java类
    2.Sychronized 无法判断获取锁的状态,Lock锁可以判断是否获取到了锁.
    3.Sychronized 会自动释放锁lock必须手动释放锁,如果不释放锁,死锁
    4.Sychronized 线程一(获得锁,阻塞),线程二(等待,傻傻的等),Lock锁就不一定会等待下去.
    5.Sychronized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以中断锁,非公平(可以自己设置)
    6.Sychronized 适合锁少量的代码的同步问题,Lock适合锁大量的代码同步问题.

    2、java真的可以开启线程吗?不可以

    private native void start0();
    //本地方法,调用底层c++,java运行在虚拟机之上,无法直接操作硬件,由c++开启多线程

    3、线程的状态:6个

    public enum State {
    //就绪
    NEW,
    //运行
    RUNNABLE,
    //阻塞
    BLOCKED,
    //等待
    WAITING,
    //超时等待
    TIMED_WAITING,
    //终止
    TERMINATED;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4、wait与sleep的区别

    来自不同的类wait=》Object sleep=》Thread
    wait释放锁,sleep抱着锁睡觉
    wait必须在同步代码快中,sleep可以在任何地方睡觉
    wait不需要捕获异常,sleep需要捕获异常(可能发生超时等待)

    5、JMM

    volatile是java虚拟机提供的轻量级的同步机制
    1.保证可见性
    2.不保证原子性
    3.由于内存屏障,禁止指令重排
    什么是JMM
    JMM:java的内存模型,不存在的东西,概念,约定
    关于JMM的一些同步的约定:
    1.线程解锁前,必须把共享变量立刻刷回主存
    2.线程枷锁前,必须读取主存中的最新值到工作的内存中
    3.加锁和解锁是同一把锁
    线程:工作内存 ,主内存
    在这里插入图片描述

    volatile关键字

    也就是取使用这个变量的时候,就会去主内存下刷一下值 要结合锁去使用
    不是线程安全,一般用于原子性操作 工作流程JMM 取值到线程自己的工作空间 原子性操作被volatile修饰加锁时候是安全的

    原子性操作下加锁安全 列入单例下不加关键字的话就会出现原本有的话 但是工作空间没有 就会新创建对象赋值 就不能达到单例的效果

    class Singleton {
     // 不是一个原子性操作
     //private static Singleton instance;
     //改进,Volatile 可以保持可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!
     private static volatile Singleton instance;
    
     // 构造器私有化
     private Singleton() {
     }
    
     // 提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题,同时保证了效率, 推荐使用
     //如果不加关键字  那么  线程a中的和线程b中通过jmm刷内存   a赋值操作了   但是b没有去刷新内存  所以b是空的   又创建一次对象赋值操作
     public static Singleton getInstance() {
      if (instance == null) {
       synchronized (Singleton.class) {
        if (instance == null) {
         instance = new Singleton();
        }
       }
      }
      return instance;
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    证明非原子性操作不是线程安全
    在这里插入图片描述

    public class Test {
    
        
    
    // volatile不保证原子性
        // 原子性:保证数据一致性、完整性
        volatile int number = 0;
    
        public void addPlusPlus() {
            number++;
        }
    
        public static void main(String[] args) {
            Test volatileAtomDemo = new Test();
            for (int j = 0; j < 20; j++) {
                new Thread(() -> {
                    for (int i = 0; i < 1000; i++) {
                        volatileAtomDemo.addPlusPlus();
                    }
                }, String.valueOf(j)).start();
            }// 后台默认两个线程:一个是main线程,一个是gc线程
            System.out.println(Thread.currentThread().getName() +
                    " final number result = " + volatileAtomDemo.number);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    juc是

    包名首字母 java.util.concurrent

    ThreadLocal

    当前前程只能获取当前线程的值,内部有个ThreadLocalMap
    应用场景:
    1、每个线程需要有自己单独的实例
    2、实例需要在多个方法中共享,但不希望被多线程共享

  • 相关阅读:
    linux内核初始化成功后是如何过渡到android初始化的
    dify接入文心一言出错
    2022腾讯云年终双十一配置表汇总-云服务器CVM+轻量应用服务器
    记录因为端口号使用6000,造成浏览器GET请求无响应
    《Flask Web 开发指南 pt.2》
    基于stm32单片机的物联网WiFi智能快递柜设计
    java毕业生设计中小学家校通系统计算机源码+系统+mysql+调试部署+lw
    EasyCVR视频汇聚平台云计算技术核心优势:高效、灵活与可扩展性深度解读
    疫情可视化part2
    作为一个十年卷王,告诫你们年轻人应该如何才能认清自己的价值
  • 原文地址:https://blog.csdn.net/weixin_49390750/article/details/126347354