SpringBoot 多线程异步@Async + 信号量 Semaphore

Java 信号量 Semaphore

信号量是一种多线程同步工具,用于控制同时访问某个资源的线程数量。

信号量(Semaphore),在多线程环境下用于协调各个线程, 以保证它们能够正确、合理的使用公共资源.

比如 ClassA,B,C,D 都需要访问资源ClassCommon, 但是同时只能有2个线程同时访问资源ClassCommon时,就可以把ClassCommon包裹在Semaphore中

线程执行顺序 同一时间最多有3个线程在执行任务,其余线程会等待许可释放。

Semaphore semaphore = new Semaphore(3); //定义一个信号量,最多允许3个线程同时访问。  
semaphore.acquire(); //获取一个许可,如果没有可用许可,线程会阻塞。  
semaphore.release(); //释放一个许可,允许其他线程继续执行。

 

请用SpringBoot+线程池+Semaphore 实现以下需求

定义类ClassA, 实现方法methodA, 输出接收到的参数,并睡眠30s

定义类ClassB, 实现方法methodB, 顺序调用ClassA.methodA

定义类ClassC, 实现方法methodC, 开多线程调用ClassA.methodA,同时执行最大线程数10

并且同时调用ClassA.methodA方法的线程数最大是10个,该线程数包括ClassB的调用和ClassC的调用.

分析:

在这个场景中ClassA.methodA就是公共资源。

这个代码使用了ExecutorService来管理线程池,并使用Semaphore来限制同时处理的请求数量为10。

其余的请求会被阻塞,直到有处理完成的请求释放信号量

ThreadPoolTaskExecutor提供线程池支持, 实现异步方法ClassB.methodB

@Async注解实现异步调用ClassA.methodA

Semaphore控制同时调用ClassA.methodA的线程数不超过10。

定义线程池和信号量,开启异步

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;

@Configuration
@EnableAsync
public class ThreadPoolAndSemaphoreConfig {

    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数
        executor.setCorePoolSize(10);
        // 最大线程数
        executor.setMaxPoolSize(50);
        // 队列容量
        executor.setQueueCapacity(100);
        // 线程存活时间(秒)
        executor.setKeepAliveSeconds(60);
        // 线程名称前缀
        executor.setThreadNamePrefix("AsyncExecutor-");
        // 拒绝策略:抛出异常
        executor.setRejectedExecutionHandler(new java.util.concurrent.ThreadPoolExecutor.AbortPolicy());
        // 初始化线程池
        executor.initialize();
        return executor;
    }

    @Bean
    public Semaphore semaphore() {
        // 限制同时访问公共的线程数量为 10
        return new Semaphore(10);
    }
}

 

ClassA,  用信号量控制

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.Semaphore;

@Service
public class ClassA {
    private static final Logger logger = LoggerFactory.getLogger(ClassA.class);

    @Autowired
    private Semaphore semaphore;

    public void methodA(String param) throws InterruptedException {

        try {
            logger.info("before");
            semaphore.acquire(); // Acquire a permit
            logger.info("acquired");
            try {
                logger.info("Executing methodA with param: {}", param);
                Thread.sleep(30000); // Simulate a long-running task

            } finally {
                semaphore.release(); // Release the permit
                logger.info("released");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Thread interrupted", e);
        }
    }
}

 

定义ClassB, 同步call ClassA

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.Semaphore;

@Service
public class ClassB {

    @Autowired
    private ClassA classA;

    @Autowired
    private Semaphore semaphore;

    private static final Logger logger = LoggerFactory.getLogger(ClassB.class);

    public void methodB(String param) throws InterruptedException {
        classA.methodA(param);
    }
}

 

定义ClassC, 异步callClassA

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.Semaphore;

@Service
public class ClassC {

    @Autowired
    private ClassA classA;

    @Autowired
    private Semaphore semaphore;

    private static final Logger logger = LoggerFactory.getLogger(ClassC.class);

    @Async("taskExecutor")
    public void methodC(String param) throws InterruptedException {
        classA.methodA(param);
    }
}

 

测试controller

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Autowired
    private ClassC classC;

    @Autowired
    private ClassB classB;

    private static final Logger logger = LoggerFactory.getLogger(ClassB.class);


    @GetMapping("/testSequential")
    public String testSequential() throws InterruptedException {
        logger.info("testSequential");
        classB.methodB("SequentialCall");
        return "Sequential call completed";
    }

    @GetMapping("/testConcurrent")
    public String testConcurrent() throws InterruptedException {
        logger.info("testConcurrent");
        classC.methodC("ConcurrentCall-");

        return "Concurrent calls initiated";
    }
}

 

同时处理ClassA请求的个数 =  Semaphore可用量  

同时处理ClassB请求的个数 = min(线程池可用线程,Semaphore可用量)

posted on 2020-06-11 15:32  dreamstar  阅读(240)  评论(0)    收藏  举报