java多线程分布式锁

springboot整合@Async

@Async注解作用:实现异步方法调用
应用场景列子:多线程计费问题
同步:多个线程访问同一个资源 A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求不到,怎么办,A线程只能等待下去
异步:多个线程访问同一个资源 A线程要请求某个资源,但是此资源正在被B线程使用中,因为没有同步机制存在,A线程仍然请求的到,A线程无需等待

启动类

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

/**
 * @program:
 * @description:
 * @author:
 * @create:
 **/
@SpringBootApplication
@EnableAsync
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Controller层

package com.example.demo.controller;

import com.example.demo.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.Future;

/**
 * @program:
 * @description:
 * @author:
 * @create:
 **/
@RestController
public class AsyncController {

    @Autowired
    private AsyncService asyncService;

    @RequestMapping(value = "/test1",method = RequestMethod.GET)
    public String asyncTask1() throws InterruptedException {
        long start = System.currentTimeMillis();
        asyncService.doTaskOne1();
        asyncService.doTaskTwo1();
        asyncService.doTaskThree1();
        return String.format("任务执行成功,耗时{%s}", System.currentTimeMillis() - start);
    }

    @RequestMapping(value = "/test2",method = RequestMethod.GET)
    public String asyncTask2() throws InterruptedException {
        long start = System.currentTimeMillis();
        Future<String> task1 = asyncService.doTaskOne2();
        Future<String> task2 = asyncService.doTaskTwo2();
        Future<String> task3 = asyncService.doTaskThree2();

        // 三个任务都调用完成,退出循环等待
        while (!task1.isDone() || !task2.isDone() || !task3.isDone()) {
            Thread.sleep(1000);
        }

        long end = System.currentTimeMillis();
        System.out.println("任务全部完成,总耗时:" + (end - start) + "毫秒");
        return String.format("任务执行成功,耗时{%s}", System.currentTimeMillis() - start);
    }
}

Service层

package com.example.demo.service;

import org.springframework.scheduling.annotation.Async;

import java.util.concurrent.Future;

/**
 * @program: demo
 * @description:
 * @author:
 * @create:
 **/
public interface AsyncService {

    @Async
    void doTaskOne1() throws InterruptedException;

    @Async
    void doTaskTwo1() throws InterruptedException;

    @Async
    void doTaskThree1() throws InterruptedException;

    @Async
    Future<String> doTaskOne2() throws InterruptedException;

    @Async
    Future<String> doTaskTwo2() throws InterruptedException;

    @Async
    Future<String> doTaskThree2() throws InterruptedException;
}

Impl层

package com.example.demo.impl;

import com.example.demo.service.AsyncService;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import java.util.Random;
import java.util.concurrent.Future;

/**
 * @program:
 * @description:
 * @author:
 * @create:
 **/
@Service
public class AsyncServiceImpl implements AsyncService {

    private static Random random = new Random();

    @Override
    public void doTaskOne1() throws InterruptedException {
        System.out.println("开始做任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
    }

    @Override
    public void doTaskTwo1() throws InterruptedException {
        System.out.println("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
    }

    @Override
    public void doTaskThree1() throws InterruptedException {
        System.out.println("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
    }

    @Override
    public Future<String> doTaskOne2() throws InterruptedException {
        System.out.println("开始做任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
        return new AsyncResult<>("任务一完成");
    }

    @Override
    public Future<String> doTaskTwo2() throws InterruptedException {
        System.out.println("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
        return new AsyncResult<>("任务二完成");
    }

    @Override
    public Future<String> doTaskThree2() throws InterruptedException {
        System.out.println("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
        return new AsyncResult<>("任务三完成");
    }
}

test1测试结果
在这里插入图片描述
在这里插入图片描述

test2测试结果
在这里插入图片描述
在这里插入图片描述
@Async用于多线程的 可以加上注解多线程
在service层加上value例如:@Async(value = “asyncServiceExecutor”)

修改后
package com.example.demo.service;

import org.springframework.scheduling.annotation.Async;

import java.util.concurrent.Future;

/**
 * @program: demo
 * @description:
 * @author:
 * @create:
 **/
public interface AsyncService {

    @Async(value = "asyncServiceExecutor")
    void doTaskOne1() throws InterruptedException;

    @Async(value = "asyncServiceExecutor")
    void doTaskTwo1() throws InterruptedException;

    @Async(value = "asyncServiceExecutor")
    void doTaskThree1() throws InterruptedException;

    @Async(value = "asyncServiceExecutor")
    Future<String> doTaskOne2() throws InterruptedException;

    @Async(value = "asyncServiceExecutor")
    Future<String> doTaskTwo2() throws InterruptedException;

    @Async(value = "asyncServiceExecutor")
    Future<String> doTaskThree2() throws InterruptedException;
}
线程池方法
package com.cudatec.infra.acm.config;

import ch.qos.logback.classic.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@EnableAsync
@Configuration
public class ExecutorConfig {
    private static final Logger logger = (Logger) LoggerFactory.getLogger(ExecutorConfig.class);

    @Value("${async.executor.thread.core_pool_size}")
    private int corePoolSize;
    @Value("${async.executor.thread.max_pool_size}")
    private int maxPoolSize;
    @Value("${async.executor.thread.queue_capacity}")
    private int queueCapacity;
    @Value("${async.executor.thread.name.prefix}")
    private String namePrefix;
    @Value("${async.executor.thread.await.termination.seconds}")
    private  String awaitTerminationSeconds;
    @Value("${async.executor.thread.alive.seconds}")
    private  int aliveSeconds;

    @Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        logger.info("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);
        //配置线程池中的线程的名称前缀
        executor.setKeepAliveSeconds(aliveSeconds);
        //用来设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean,这样这些异步任务的销毁就会先于Redis线程池的销毁
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //该方法用来设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
        executor.setAwaitTerminationSeconds(60);
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }
}

package com.cudatec.infra.acm.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.concurrent.ListenableFuture;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
    private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);

    private void showThreadPoolInfo(String prefix) {
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

        if (null == threadPoolExecutor) {
            return;
        }

        logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                prefix,
                threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(),
                threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());
    }

    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute");
        super.execute(task);
    }

    @Override
    public void execute(Runnable task, long startTimeout) {
        showThreadPoolInfo("2. do execute");
        super.execute(task, startTimeout);
    }

    @Override
    public Future<?> submit(Runnable task) {
        showThreadPoolInfo("1. do submit");
        return super.submit(task);
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        showThreadPoolInfo("2. do submit");
        return super.submit(task);
    }

    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
        showThreadPoolInfo("1. do submitListenable");
        return super.submitListenable(task);
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        showThreadPoolInfo("2. do submitListenable");
        return super.submitListenable(task);
    }
}

线程配置
application.properties

# 异步线程配置
# 配置核心线程数
async.executor.thread.core_pool_size = 50
# 配置最大线程数
async.executor.thread.max_pool_size = 100
# 配置队列大小
async.executor.thread.queue_capacity = 99999
# 配置线程池中的线程的名称前缀
async.executor.thread.name.prefix = async-service-
# 配置线程池中的线程等待时间后自动关闭
async.executor.thread.await.termination.seconds = 60
async.executor.thread.alive.seconds = 60

异步缺点
A线程要请求某个资源,但是此资源正在被B线程使用中,因为没有同步机制存在,A线程仍然请求的到,A线程无需等待的问题(例子:一个客户发起一次扣费,由于A B线程都请求到 出现多笔扣费)

解决方法:锁(避免重复记录)

Impl层 方法上加上注解
@DistributedLock(param = “锁对象参数,不设置位置默认第一个”, fairLock = true, tryLock = true)

	//锁的是用户User的第一个参数userId
    @Override
    @DistributedLock(param = "userId", fairLock = true, tryLock = true)
    public void doTaskOne1(User user) throws InterruptedException {
    }
package com.example.demo.common.annotation;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

/**
 * @program:
 * @description:
 * @author:
 * @create:
 **/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {
    /**
     * 锁的名称。
     * 如果lockName可以确定,直接设置该属性。
     */
    String lockName() default "";

    /**
     * lockName后缀
     */
    String lockNamePre() default "";
    /**
     * lockName后缀
     */
    String lockNamePost() default "lock";

    /**
     * 获得锁名时拼接前后缀用到的分隔符
     * @return
     */
    String separator() default ".";
    /**
     * <pre>
     *     获取注解的方法参数列表的某个参数对象的某个属性值来作为lockName。因为有时候lockName是不固定的。
     *     当param不为空时,可以通过argNum参数来设置具体是参数列表的第几个参数,不设置则默认取第一个。
     * </pre>
     */
    String param() default "";
    /**
     * 将方法第argNum个参数作为锁
     */
    int argNum() default 0;
    /**
     * 是否使用公平锁。
     * 公平锁即先来先得。
     */
    boolean fairLock() default false;
    /**
     * 是否使用尝试锁。
     */
    boolean tryLock() default false;
    /**
     * 最长等待时间。
     * 该字段只有当tryLock()返回true才有效。
     */
    long waitTime() default 30L;
    /**
     * 锁超时时间。
     * 超时时间过后,锁自动释放。
     * 建议:
     *   尽量缩简需要加锁的逻辑。
     */
    long leaseTime() default 5L;
    /**
     * 时间单位。默认为秒。
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值