并发工具类

JUC工具

JUC(Java Util Concurrent)是Java提供的一组并发工具和类,主要用于处理多线程编程中的常见问题。JUC包的主要目标是促进并发编程,并提供更简洁和高效的方法来管理线程、锁、执行任务等。

并发工具类

在JUC并发包里提供了几个非常有用的并发容器和并发工具类,供我们在多线程开发中进行使用。

CountDownLatch

CountDownLatch被称之为倒计数器CountDownLatch允许一个或多个线程等待其他线程完成操作以后,再执行当前线程。

CountDownLatch中count down是倒着数数的意思;CountDownLatch是通过一个计数器来实现的,每当一个线程完成了自己的任务后,可以调用countDown()方法让计数器-1,当计数器到达0时,调用CountDownLatch的await()方法的线程阻塞状态解除,继续执行。

CountDownLatch的相关方法:

public CountDownLatch(int count)						// 初始化一个指定计数器的CountDownLatch对象
public void await() throws InterruptedException			// 让当前线程等待,直到计数器值为0是解除堵塞状态
public void countDown()									// 计数器进行减1

比如我们在主线程需要开启2个其他线程,当其他的线程执行完毕以后我们再去执行主线程,针对这个需求我们就可以使用CountDownLatch来进行实现。

import java.util.concurrent.CountDownLatch;  

public class CountDownLatchExample {  
    public static void main(String[] args) {  
        // 创建一个 CountDownLatch,计数器的初始化值为2,表示需要等待两个线程  
        CountDownLatch latch = new CountDownLatch(2);  

        // 创建两个子线程  
        Thread thread1 = new Thread(() -> {  
            try {  
                // 模拟工作  
                System.out.println("Thread 1 is working...");  
                Thread.sleep(2000); // 模拟耗时操作  
                System.out.println("Thread 1 finished.");  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            } finally {  
                // 当前线程完成工作,计数器减1  
                latch.countDown();  
            }  
        });  

        Thread thread2 = new Thread(() -> {  
            try {  
                // 模拟工作  
                System.out.println("Thread 2 is working...");  
                Thread.sleep(3000); // 模拟耗时操作  
                System.out.println("Thread 2 finished.");  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            } finally {  
                // 当前线程完成工作,计数器减1  
                latch.countDown();  
            }  
        });  

        // 启动线程  
        thread1.start();  
        thread2.start();  

        try {  
            // 主线程在这里等待,直到计数器变为0  
            latch.await();  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  

        // 当两个线程都完成时,继续执行主线程的后续操作  
        System.out.println("Both threads are finished. Main thread is continuing...");  
    }  
}

CyclicBarrier

CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。

它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。

CyclicBarrier的相关方法

// 用于在线程到达屏障时,优先执行barrierAction,方便处理更复杂的业务场景
public CyclicBarrier(int parties, Runnable barrierAction)   
// 每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞
public int await()											

用于让一组线程互相等待,直到达到某个公共的屏障点。在达到这个屏障点之前,所有的线程都必须调用 await() 方法。一旦所有线程都到达了这个屏障,所有线程会被释放并继续执行。

import java.util.concurrent.BrokenBarrierException;  
import java.util.concurrent.CyclicBarrier;  

public class CyclicBarrierExample {  
    public static void main(String[] args) {  
        // 创建一个 CyclicBarrier,设置等待的线程数量为3  
        CyclicBarrier barrier = new CyclicBarrier(3, () -> {  
            // 这个任务会在所有线程到达屏障时执行  
            System.out.println("All threads have reached the barrier. Let's proceed.");  
        });  

        // 创建3个线程  
        Runnable task = () -> {  
            try {  
                System.out.println(Thread.currentThread().getName() + " is working...");  
                // 模拟工作  
                Thread.sleep((long) (Math.random() * 3000));  
                System.out.println(Thread.currentThread().getName() + " is ready to meet at the barrier.");  
                
                // 等待其他线程到达屏障  
                barrier.await();  
                
                // 到达屏障后继续执行  
                System.out.println(Thread.currentThread().getName() + " has crossed the barrier.");  
            } catch (InterruptedException | BrokenBarrierException e) {  
                e.printStackTrace();  
            }  
        };  

        // 启动3个线程  
        Thread thread1 = new Thread(task, "Thread 1");  
        Thread thread2 = new Thread(task, "Thread 2");  
        Thread thread3 = new Thread(task, "Thread 3");  

        thread1.start();  
        thread2.start();  
        thread3.start();  
    }  
}

Semaphore

Semaphore字面意思是信号量的意思,它的作用是控制访问特定资源的线程数目

Semaphore的常用方法

public Semaphore(int permits)						permits 表示许可线程的数量
public void acquire() throws InterruptedException	表示获取许可
public void release()								表示释放许可

比如给定一个资源数目有限的资源池,假设资源数目为N,每一个线程均可获取一个资源,但是当资源分配完毕时,后来线程需要阻塞等待,直到前面已持有资源的线程释放资源之后才能继续。

import java.util.concurrent.Semaphore;  

public class SemaphoreExample {  
    // 创建一个 Semaphore,允许最多2个线程同时访问  
    private static final Semaphore semaphore = new Semaphore(2);  

    public static void main(String[] args) {  
        // 创建5个线程  
        for (int i = 0; i < 5; i++) {  
            new Thread(new Worker(i)).start();  
        }  
    }  

    static class Worker implements Runnable {  
        private final int threadNumber;  

        public Worker(int threadNumber) {  
            this.threadNumber = threadNumber;  
        }  

        @Override  
        public void run() {  
            try {  
                // 获取许可证  
                System.out.println("Thread " + threadNumber + " is trying to acquire a permit.");  
                semaphore.acquire();  
                System.out.println("Thread " + threadNumber + " has acquired a permit.");  

                // 模拟工作  
                Thread.sleep(2000);  

                // 释放许可证  
                System.out.println("Thread " + threadNumber + " is releasing the permit.");  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            } finally {  
                // 释放许可证,即使发生异常也要确保释放  
                semaphore.release();  
                System.out.println("Thread " + threadNumber + " has released the permit.");  
            }  
        }  
    }  
}

JMM(Java内存模型)

概述:JMM(Java Memory Model,Java内存模型),是java虚拟机规范中所定义的一种内存模型。

JMM描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节。

特点:

1、所有的线程共享变量都存储于主内存(计算机的RAM)。

2、每个线程拥有自己的工作内存,保留了被线程使用的变量工作副本

3、线程对变量的所有的操作(读/写)都必须在自己的工作内存中完成,而不能直接读写主内存中的变量。

4、不同线程之间也不能直接访问对方工作内存中的变量,线程间变量的值的传递需要通过主内存完成。

image-20230308144950989

volatile关键字

volatile 是 Java 中的一个关键字,用于变量的声明。它的主要作用是保证多线程环境中的可见性和防止指令重排序。

具体来说,volatile 关键字能确保被它修饰的变量在多个线程之间的可见性,以及在读取该变量时不会被优化掉,从而提供了一种轻量级的同步方式。

可见性

在没有使用 volatile 的情况下,线程可能会在其本地缓存中保存变量的值,而不是从主内存中读取。当一个线程修改了一个共享变量的值时,其他线程可能无法立即看到这个修改。这会导致线程间的状态不一致。

使用 volatile 修饰的变量,Java 保证了以下几点:

当一个线程修改了被 volatile 修饰的变量的值时,其他线程能够立即看到这个变化。这是通过在写入时将新的值刷新到主内存中,并在读取时从主内存中获取最新的值来实现的。

防止指令重排序

Java编译器和运行时可以对程序进行优化,包括指令重排序,以提高执行效率。使用 volatile 可以防止这些优化,确保对 volatile 变量的写入和后续对该变量的读取不会被重排序。

注意事项

不具备原子性volatile 仅仅保证可见性和防止重排序,但不保证对变量的操作是原子的。例如,对于递增操作(如 count++),要用锁或其他方式保证原子性。

适用场景:可以用 volatile 来标记那些只需要确保可见性而不是复杂操作的情况下,例如标志位、状态变量等。对于需要复合操作的多线程场景,应该考虑使用 synchronizedLock 等机制。

public class VolatileThread extends Thread {

    // 定义成员变量
    private volatile boolean flag = false ;
    public boolean isFlag() { return flag;}

    @Override
    public void run() {

        // 线程休眠1秒
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 将flag的值更改为true
        this.flag = true ;
        System.out.println("flag=" + flag);

    }
}

控制台输出结果

flag=true
执行了======
执行了======
执行了======
....

并发编程三特性

可见性

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改的值。

volatile关键字修饰的共享变量是可以保证可见性的。

原子性

概述:所谓的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行,多个操作是一个不可以分割的整体volatile不保证对变量的操作是原子性的

问题处理

1、加锁 synchronized、lock

2、使用原子类 ==>AtomicInterger

有序性

有序性:代码编写顺序和代码执行顺序一致。

计算机在执行程序时,为了提高性能,编译器和处理器有时会对指令重排

原子类与CAS算法

原子类(AtomicInteger)

AtomicInteger原理: 自旋锁 + CAS算法

概述:java 从 JDK1.5 开始提供了 java.util.concurrent.atomic包(简称Atomic包)。

这个包中的 原子操作类 提供了一种 线程安全 地更新一个变量的方式,且简单高效。

因为变量的类型有很多种,Atomic包里一共提供了13个类,属于4种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。

使用原子的方式更新基本类型,使用原子的方式更新基本类型Atomic包提供了以下3个类:

AtomicBoolean: 原子更新布尔类型

AtomicInteger: 原子更新整型

AtomicLong: 原子更新长整型

AtomicInteger的常用方法:

AtomicInteger 是 Java 中用于处理整数的一个原子类,它提供了一种无锁的方式来进行整数值的更新,使得多线程环境下对整数的安全操作变得更为简单。

public AtomicInteger():	   				初始化一个默认值为0的原子型Integer
public AtomicInteger(int initialValue): 初始化一个指定值的原子型Integer

int get():   			 				 获取当前值
int getAndIncrement():      			 以原子方式将当前值加1,注意,这里返回的是自增前的值。 i++
int incrementAndGet():     				 以原子方式将当前值加1,注意,这里返回的是自增后的值。 ++i
int addAndGet(int data):				 以原子方式将输入数值与实例中值(AtomicInteger里的value)相加,并返回结果。
int getAndSet(int value):   			 以原子方式设置为newValue的值,并返回旧值。

基本使用

public class AtomicIntegerDemo {
    // 原子型Integer
    public static void main(String[] args) {
        // 构造方法
        // public AtomicInteger():初始化一个默认值为0的原子型Integer
        // AtomicInteger atomicInteger = new AtomicInteger() ;
        // System.out.println(atomicInteger);

        // public AtomicInteger(int initialValue): 初始化一个指定值的原子型Integer
        AtomicInteger atomicInteger = new AtomicInteger(5) ;
        System.out.println(atomicInteger);

        // 获取值
        System.out.println(atomicInteger.get());

        // 以原子方式将当前值加1,这里返回的是自增前的值
        System.out.println(atomicInteger.getAndIncrement());
        System.out.println(atomicInteger.get());

        // 以原子方式将当前值加1,这里返回的是自增后的值
        System.out.println(atomicInteger.incrementAndGet());

        // 以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果
        System.out.println(atomicInteger.addAndGet(8));

        // 以原子方式设置为newValue的值,并返回旧值
        System.out.println(atomicInteger.getAndSet(20));
        System.out.println(atomicInteger.get());
    }
}

AtomicInteger源码分析

public final class AtomicInteger extends Number implements Serializable {  
    private static final long serialVersionUID = 6214790243416808450L;  
    private final AtomicIntegerFieldUpdater updater;  
    private static final Unsafe unsafe = Unsafe.getUnsafe();  
    private static final long valueOffset;  
    private volatile int value;  

    static {  
        try {  
            valueOffset = unsafe.objectFieldOffset  
                (AtomicInteger.class.getDeclaredField("value"));  
        } catch (Exception ex) { throw new Error(ex); }  
    }  
    
    public AtomicInteger() {  
        this.value = 0;  
    }  

    public AtomicInteger(int initialValue) {  
        this.value = initialValue;  
    }  

    // 省略其他方法  
}
  • volatile 关键字value 字段被声明为 volatile,这确保了多个线程对 value 的读写操作的可见性。
  • Unsafe 类:使用内部类 Unsafe 来进行低级的内存操作,提供 CAS 操作。

CAS 方法:compareAndSet

javapublic final boolean compareAndSet(int expect, int update) {  
   return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  
}  
  • compareAndSwapInt:这个方法的逻辑是:如果当前 value 等于 expect,就将 value 更新为 update。这是一种原子操作,保证了在并发情况下的安全性。

增加和减少的方法:incrementAndGetdecrementAndGet

javapublic final int incrementAndGet() {  
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;  
}  

public final int decrementAndGet() {  
    return unsafe.getAndAddInt(this, valueOffset, -1) - 1;  
}  
  • incrementAndGet:通过 getAndAddInt 增加 1,并返回更新后的值。
  • decrementAndGet:减少 1,并返回更新后的值。

UnSafe类

Unsafe类:jdk提供的一个不安全的类,平时开发时,肯定不会使用这个类。

该类用于直接去操作主内存。 (某个地址位置的变量)
该类中提供了CAS方法,用于操作主内存中的共享变量。

public final class Unsafe {
    
    // Unsafe类中的getAndAddInt方法
    public final int getAndAddInt(Object o, long offset, int delta) {
        
        int v;
        
        // do...while就是自旋操作,当CAS成功以后,循环结束
        do {
            // 获取AtomicInteger类中所封装的int类型的值,就相当于旧的预期值A
            v = getIntVolatile(o, offset); 
            
            // 调用本类的weakCompareAndSetInt方法实现比较在交换; o: AtomicInteger对象, v: 相当于旧的预期值A, v + delta:新值B
        } while (!weakCompareAndSetInt(o, offset, v, v + delta));
        
        return v;
    }
    
    // Unsafe类中的weakCompareAndSetInt方法
    public final boolean weakCompareAndSetInt(Object o, long offset, int expected, int x) {
        return compareAndSetInt(o, offset, expected, x);
    }

    // 本地方法,调用CPU指令实现CAS
    public final native boolean compareAndSetInt(Object o, long offset, int expected, int x);
    
}

CAS算法

CAS的全称是: Compare And Swap(比较再交换),是一种对内存中的共享数据进行操作的一种特殊指令

CAS可以将 read-modify-write 转换为原子操作,这个原子操作直接由CPU保证

CAS有3个操作数(参数):内存值V(内存中的一个变量),旧的预期值E,要修改的新值N。

  • V:变量的值
  • E:期望值(Expected Value)
  • N:新值(New Value)

CAS的执行过程如下:

  1. 算法会检查变量V的当前值是否等于期望值E。
  2. 如果相等,CAS操作将变量V的值更新为新值N。
  3. 如果不相等,变量V的值不变,并且操作返回V的当前值。

举例说明:

  1. 在内存值V,存储着值为10的变量。

1571817059527

  1. 此时线程1想要把变量的值增加1。对线程1来说,旧的预期值 A = 10 ,要修改的新值 B = 11。

1571817085047

  1. 在线程1要提交更新之前,另一个线程2抢先一步,把内存值V中的变量值率先更新成了11。

1571817628904

  1. 线程1开始提交更新,首先进行A和内存值V的实际值比较(Compare),发现A不等于V的值,提交失败。

1571818176635

  1. 线程1重新获取内存值V作为当前A的值,并重新计算想要修改的新值。此时对线程1来说,A = 11,B = 12。这个重新尝试的过程被称为自旋

1571818465276

  1. 这一次比较幸运,没有其他线程改变V的值。线程1进行Compare,发现A和V的值是相等的。

1571818597998

  1. 线程1进行SWAP,把内存V的值替换为B,也就是12。

1571818747880

举例说明:这好比春节的时候抢火车票,下手快的会抢先买到票,而下手慢的可以再次尝试,直到买到票。

CAS缺点

1、在并发量比较高的情况下,如果反复尝试更新某个变量,却又一直更新不成功,会给CPU带来较大的压力。

2、ABA问题:当变量从A修改为B再修改回A时,变量值等于期望值A,但是无法判断是否修改,CAS操作在ABA修改后依然成功。

3、CAS机制只保证一个变量的原子性操作,而不能保证整个代码块的原子性。

AQS(抽象队列同步器)-了解

在Java的java.util.concurrent(简称JUC)包中,AbstractQueuedSynchronizer(AQS,抽象队列同步器)是一个核心的基础类,它为依赖于先进先出(FIFO)等待队列的阻塞锁和相关的同步器(如SemaphoreCountDownLatchReentrantLockReentrantReadWriteLock等)提供了一个框架。

AQS是一个基于状态(通常使用一个volatile的int类型的变量state表示)的同步器,它使用了一个内部的FIFO队列来完成等待线程的管理。这个队列被称为CLH队列,用于在获取不到资源的线程之间进行排队。

image-20230309192701029

作用:

  • 同步状态管理:AQS使用一个volatile的int类型的变量state来维护同步状态。state为0表示没有线程占用资源,非0表示有线程占用资源。
  • 队列管理:AQS内部维护了一个等待队列,当线程获取不到资源时,会加入到这个队列中进行等待,直到被唤醒或超时。
  • 独占模式和共享模式:AQS支持两种模式的资源访问控制:独占模式和共享模式。独占模式意味着只有一个线程能访问执行;共享模式允许多个线程同时访问执行。

AQS同步队列的head节点是一个空节点,没有记录线程node.thread=null,其后继节点才是实质性的有线程的节点。

AQS两种模式

独占模型

典型的实现类ReentrantLock==》state初始值0,加锁成功,则将state+1

独占模式下时,其他线程试图获取该锁将无法取得成功,只有一个线程能执行,如ReentrantLock采用独占模式。

image-20241018184608865

线程获取锁(非公平)的流程:

  1. state初始值=0,并且被volatile关键字进行修饰。加锁成功就是将state由0改成1,如果是锁的重入则继续+1…
  2. 首先通过cas的方式将state由期望值0,改成1,如果成功,则说明加锁成功,并且exclusiveOwnerThread属性记录当前线程加锁成功的线程。
  3. 如果上一步直接加锁失败,说明state现在不等于0,接下来判断当前线程是否是正在持有锁的线程,如果是则说明当前线程在进行锁的重入,此时将state+1即可。
  4. 如果上一步不是锁的重入,再一次直接尝试获取锁。
  5. 如果上一步再次获取锁又失败了,此时当前线程就需要被封装成一个Node对象加入到双向链表的末尾。

线程获取锁(公平)的流程:

  1. 当锁空闲时,也就是state=0,先判断等待队列中是否存在等待锁的线程,如果队列中没有现成正在排队,此时当前线程就不需要去加入到队列中,而是可以直接去加锁
  2. 如果上一步判断state不为空,说明锁并非空闲(有线程正在持有锁),接下来判断正在持有锁的线程 是 否就是当前线程,如果是,则说明当前线程在进行锁的重入,state+1即可
  3. 如果上一步不是锁的重入会先再一次判断:如果此时锁空闲并且等待队列为空,可以直接尝试cas去加锁。
  4. )如果上一步锁并非空闲或cas失败,当前线程只能封装成Node对象加入到等待队列。

线程释放锁的流程(公平非公平一至):

  1. 判断当前线程是否是正在持有锁的线程,如果不是,则抛出异常。
  2. 获取state并且-1(更新state),如果state=0,则将exclusiveOwnerThread属性设置为null, 当state为0时,说明当前锁被彻底释放。
  3. 当前线程如果是正在持有锁的线程,将stat-1之后,如果不是0,说明当前线程之前获取锁的过程中有进行锁的重入。
  4. 如果释放锁之后state-1等于0了,此时将头结点的下一个节点对应的waiter属性(线程对象)进行唤醒,被唤醒后,当前节点就可以去抢占锁。

共享模型

典型的实现类Semaphore==》state初始值n(自定义),加锁成功,则将state-1

线程获取锁(非公平)的流程:

  1. 获取state值,并且-1,得到的结果表示共享锁剩余的资源数
  2. 如果-1后,此时共享锁的剩余资源<0, 说明共享锁的多个资源都被占用了,接下来将当前线程封装到一个SharedNode节点,该节点加入到等待队列中。
  3. 如果上一步state-1之后>0,通过cas的方式去修改state,如果cas失败,则自旋,重新再来一遍(重新获取state, 重新-1,重新cas)
  4. 当多次cas操作成功后,一定返回一个大于等于0的整数。加锁过程则结束。

线程获取锁(公平)的流程:
判断队列为空,则cs+自旋尝试加锁,每次自旋时都会重新判断队列是否为空如果队列不为空,当前线程就不能去加锁,而是加入到等待队列中。

线程释放锁的流程(公平非公平一至):

  1. Sempore中释放资源就是要务tate+1
  2. 获取state值,该值+1,通过cas的方式去修改state,如果cas失败,则自旋重试

异步编排

问题:查询商品详情页的逻辑非常复杂,数据的获取都需要远程调用,必然需要花费更多的时间。

假如商品详情页的每个查询,需要如下标注的时间才能完成

  1. 获取sku的基本信息 1s
    . 获取商品信息 1.5s
    . 商品最新价格 0.5s

那么,用户需要3s后才能看到商品详情页的内容。很显然是不能接受的。如果有多个线程同时完成这4步操作,也许只需要1.5s即可完成响应。

CompletableFuture可以使原本串行执行的代码,变为并行执行,提高代码执行速度。

CompletableFuture

CompletableFuture 是 Java8 引入的一个非常强大的工具类,属于 java.util.concurrent 包,它支持异步编程和并行处理,能够更方便地处理异步计算结果。通过 CompletableFuture,你可以构建一系列异步任务,并在任务完成后执行特定的操作,或处理任务的结果。

常用方法

supplyAsync:supplyAsync是创建带有返回值的异步任务。

runAsync:runAsync是创建没有返回值的异步任务。

thenApply和thenApplyAsync:表示某个任务执行完成后执行的动作,即回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中,带有返回值。

thenApply和thenApplyAsync区别在于,使用thenApply方法时子任务与父任务使用的是同一个线程,而thenApplyAsync在子任务中是另起一个线程执行任务。

thenAccept和thenAcceptAsync:表示某个任务执行完成后执行的动作,即回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中,无返回值。

thenAccep和thenAccepAsync区别在于,使用thenAccep方法时子任务与父任务使用的是同一个线程,而thenAccepAsync在子任务中可能是另起一个线程执行任务。

thenRun和thenRunAsync:表示某个任务执行完成后执行的动作,即回调方法,无入参,无返回值。

allOf / anyOf
allOf:CompletableFuture是多个任务都执行完成后才会执行,只有有一个任务执行异常,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回null。

anyOf :CompletableFuture是多个任务只要有一个任务执行完成,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回执行完成任务的结果。

1、ThreadPoolConfig

全局自定义线程池配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Configuration
public class ThreadPoolConfig {

    @Bean
    public ThreadPoolExecutor threadPoolExecutor() {
        //当前系统可用的处理器数量
        int processorsCount = Runtime.getRuntime().availableProcessors();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                processorsCount * 2,
                processorsCount * 2,
                0,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(200),
                Executors.defaultThreadFactory(),
                //new ThreadPoolExecutor.CallerRunsPolicy()
                //自定义拒绝策略
                (runnable, executor) -> {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                    }
                    //再次将拒绝任务提交给线程池执行
                    executor.submit(runnable);
                }
        );
        //线程池创建,核心线程同时创建
        //threadPoolExecutor.prestartCoreThread();
        threadPoolExecutor.prestartAllCoreThreads();
        return threadPoolExecutor;
    }
}

2、使用CompletableFuture

@Autowired
private ThreadPoolExecutor threadPoolExecutor;

public void test() {

    ItemVo itemVo = new ItemVo();
    //获取sku信息
    CompletableFuture<ProductSku> skuCompletableFuture = CompletableFuture.supplyAsync(() -> {
        //代码内容1
        return ProductSku;
    }, threadPoolExecutor);

    //获取商品信息
    CompletableFuture<Void> productComCompletableFuture = skuCompletableFuture.thenAcceptAsync(productSku ->    {  //代码内容2
    }, threadPoolExecutor);

    //获取商品最新价格
    CompletableFuture<Void> skuPriceCompletableFuture = CompletableFuture.runAsync(() -> {
        //代码内容3
    }, threadPoolExecutor);

    //获取商品详情
    CompletableFuture<Void> productDetailsComCompletableFuture = skuCompletableFuture.thenAcceptAsync(productSku -> {
       //代码内容4
    }, threadPoolExecutor);

    //获取商品规格对应商品skuId信息
    CompletableFuture<Void> skuSpecValueComCompletableFuture = skuCompletableFuture.thenAcceptAsync(productSku -> {
        //代码内容5
    }, threadPoolExecutor);

    //获取商品库存信息
    CompletableFuture<Void> skuStockVoComCompletableFuture = CompletableFuture.runAsync(() -> {
       //代码内容6
    }, threadPoolExecutor);

    //x.组合以上七个异步任务
    //多个任务都执行完成后才会执行
    CompletableFuture.allOf(
            skuCompletableFuture,
            productComCompletableFuture,
            skuPriceCompletableFuture,
            productDetailsComCompletableFuture,
            skuSpecValueComCompletableFuture,
            skuStockVoComCompletableFuture
    ).join();
    return itemVo;
}
posted @   CH_song  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示