Spring boot中使用多线程的方式有很多种,最简单的方式就是使用@Async
注解来实现。本文重点讲解多线程的使用和使用多线程注解出现循环依赖的的问题及解决方案。
必须添加
@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();
}
}
}
}
这样就可以运行了,启动后即可同时异步执行这两个方法了。
在实际使用的时候,发现并没有如此顺利。
上面的TestReceiver
类,由于都注入了一个consumerConfigs
(此行代码:private Map
)
在启动的时候,就会报错:
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
中注入了,出现了循环注入。
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
注入的对象,却出现了循环引用的错误。
第二个类中,注入consumerConfigs
的时候,加上注解@Lazy
,配合@Autowired
一起使用,完整安全注入示例:
@Autowired
@Lazy
private Map consumerConfigs;
再次启动,循环依赖问题成功解决。
Spring boot中使用多线程注解@Async
注解要配合@EnableAsync
一起使用,但在需要在多处引用由@Bean
声明创建的对象时候,在引用处,需要用注解@Lazy
配合@Autowired
来注入,来防止出现循环依赖的问题。