Springboot中使用多线程

前言:

Spring boot中使用多线程的方式有很多种,最简单的方式就是使用@Async注解来实现。本文重点讲解多线程的使用和使用多线程注解出现循环依赖的的问题及解决方案。

一.在SpringBoot的启动类开启多线程


必须添加@EnableAsync注解,来开启对多线程的支持,否则@Async注解无效。

在启动类中创建出对象,调用方法,即会创建两个线程来执行各自的方法了。

@SpringBootApplication
@EnableAsync
public class MyApplication {
    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args);
        TestReceiver t1 = context.getBean(TestReceiver.class);
        TestReceiver2 t2 = context.getBean(TestReceiver2.class);
        t1.hello();
        t2.hello();
    }

二.@Async注解创建线程


直接在方法上加上@Async注解,该方法会创建一个线程来执行。

package com.test.receiver;

import java.util.Map;

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.Component;

@Component
public class TestReceiver {

    @Autowired
    @Lazy
    private Map consumerConfigs;

    private static final Logger log = LoggerFactory.getLogger(TestReceiver.class);

    @Async
    public void hello() {
        while (true) {
            log.info("***************1111111111*******************");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在创建一个类,两个类中各有一个方法用子线程运行:

package com.test.receiver;

import java.util.Map;

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.Component;

@Component
public class TestReceiver2 {

    @Autowired
    private Map consumerConfigs;

    private static final Logger log = LoggerFactory.getLogger(TestReceiver2.class);

    @Async
    public void hello() {
        while (true) {
            log.info("************22222222*******************");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

这样就可以运行了,启动后即可同时异步执行这两个方法了。

三.遇到的循环依赖问题及解决


1.错误描述

在实际使用的时候,发现并没有如此顺利。
上面的TestReceiver类,由于都注入了一个consumerConfigs(此行代码:private Map consumerConfigs;)

在启动的时候,就会报错:

org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'testReceiver': Unsatisfied dependency expressed through field 'consumerConfigs'; 
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: 
Error creating bean with name 'testReceiver2': 
Bean with name 'testReceiver2' has been injected into other beans [testReceiver]
in its raw version as part of a circular reference, but has eventually been wrapped. 
This means that said other beans do not use the final version of the bean. 
This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.

错误提示consumerConfigs已经在testReceiver2中注入了,出现了循环注入。

2.问题定位

consumerConfigs这个Bean的创建代码:

@Bean(name = "consumerConfigs")
    public Map<String, Object> consumerProps() {
        Map<String, Object> props = new HashMap<>();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaServer);
        return props;
    }

发现在多个类中引用@Service@Component注入的Bean没有问题,但是多处引用@Bean注入的对象,却出现了循环引用的错误。

3.解决方案:

第二个类中,注入consumerConfigs的时候,加上注解@Lazy,配合@Autowired 一起使用,完整安全注入示例:

    @Autowired
    @Lazy
    private Map consumerConfigs;

再次启动,循环依赖问题成功解决。

总结


Spring boot中使用多线程注解@Async注解要配合@EnableAsync一起使用,但在需要在多处引用由@Bean声明创建的对象时候,在引用处,需要用注解@Lazy配合@Autowired来注入,来防止出现循环依赖的问题。

你可能感兴趣的:(Java)