溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

spring boot中多線程開發(fā)的注意事項(xiàng)總結(jié)

發(fā)布時間:2020-08-25 14:43:06 來源:腳本之家 閱讀:236 作者:bigfan 欄目:編程語言

前言

Springt通過任務(wù)執(zhí)行器(TaskExecutor)來實(shí)現(xiàn)多線程和并發(fā)編程。使用ThreadPoolTaskExecutor可實(shí)現(xiàn)一個基于線程池的TaskExecutor。而實(shí)際開發(fā)中任務(wù)一般是非阻礙的,即異步的,所以我們要在配置類中通過@EnableAsync 開啟對異步任務(wù)的支持,并通過實(shí)際執(zhí)行Bean的方法中使用@Async注解來聲明其是一個異步任務(wù)。

基于springboot的多線程程序開發(fā)過程中,由于本身也需要注入spring容器進(jìn)行管理,才能發(fā)揮springboot的優(yōu)勢。所以這篇文字主要用來記錄開發(fā)中兩者結(jié)合時需要注意的一些事項(xiàng)。

注意事項(xiàng)

第一步我們把線程類的實(shí)例注入sping容器進(jìn)行管理

@Configuration
@SpringBootApplication
@Import({ThreadConfig.class})
public class ThreadApp implements CommandLineRunner
{
 public static void main(String[] args) throws Exception {

  ApplicationContext app = SpringApplication.run(ThreadApp .class, args);
  //這里主要保存上下文對象實(shí)例,需要加上。SpringBootUtils類網(wǎng)上很多,可以自己搜下
  SpringBootUtils.setApplicationContext(app);

 }

 //access command line arguments
 @Override
 public void run(String... args) throws Exception {
  //do something
 }
}

//ComponentScan注解會掃描com.demo.thead下,也就是多線程類所在的包下的文件
@Configuration
@ComponentScan(basePackages = { "com.demo.thread"})
public class ThreadConfig{

}

這里使用springboot @Import 注解,把ThreadConfig里掃描到的包中帶注解的示例,如@Component等注入到spring容器當(dāng)中.

然后是線程的啟動,這里在我的業(yè)務(wù)場景中有兩種情況:

1、程序運(yùn)行時,自動啟動;

這在一般的可執(zhí)行程序里面,當(dāng)然可以直接在main函數(shù)里執(zhí)行通過代碼啟動線程。但在springboot中,我們可以使用@PostConstruct注解的方式,讓已經(jīng)注入bean容器的線程對象自啟動

@Component
public class demoThread extends Thread
{
 //注意這里,如果你沒有實(shí)現(xiàn)把多線程類的實(shí)例注入到spring容器中,這里你是無法拿到其他自動裝配的對象實(shí)例的的,這也是我們第一步的意義所在。
 @Autowired
 private XxxService xxxService;

 @PostConstruct
 public void start() {
  super.start();
 }

 public void run() {
  // Ok,在這里你就可以實(shí)現(xiàn)線程要實(shí)現(xiàn)的功能邏輯了,自然也可以直接使用裝配好的sevice對象實(shí)例。
  
 }
}

 2、在程序中,需要開啟線程時啟動,比如在從kafka接收數(shù)據(jù),開啟線程處理,當(dāng)然這種情況下也需要通過第一步,把線程類實(shí)例注入到sping容器中

private TaskThread thread;
 private ExecutorService taskPool= new ThreadPoolExecutor(
   5, 10, 1000,
   TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10),
   new ThreadPoolExecutor.CallerRunsPolicy()); 


 @KafkaListener(topics = "xxTopic")
 public void receive(ConsumerRecord<Object, Object> consumerRecord) {
   JSONObject json = JSON.parseObject(consumerRecord.value().toString());
   //通過SpringBootUtils獲取線程類的實(shí)例
   thread = SpringBootUtils.getBean(TaskThread.class);
   //啟動線程
   //new Thread(thread).start() ; 
   //向線程對象里傳值
   thread.init(i);
   //放入線程池執(zhí)行
   taskPool.execute(thread);

 }
//注意這里是否添加@Scope("prototype")注解
@Component
@Scope("prototype")
public class TaskThread implements Runnable{
 
 protected int value=0;

 @Autowired
 private XxxService xxxService;
 
 //ThreadLocal 對象,單例模式下可以保證成員變量的線程安全和獨(dú)立性。
 public ThreadLocal<Integer> valueLocal = new ThreadLocal < Integer > () {
  @Override
  protected Integer initialValue() {
   return 0;
  }
 };

 protected static final Logger LOG = LoggerFactory.getLogger(GpsTaskThread.class);
 
 @Override
 public final void run() {
  try { 
   LOG.info(value+"");
   
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

 public void init(int Value) {
  this.value=Value;
 }
}

在這里我們需要注意,TaskThread這個線程類在spirngboot中是否要添加@Scope("prototype")注解設(shè)置為多例模式還是默認(rèn)單例模式。

在單例模式下SpringBootUtils.getBean(TaskThread.class) 每次返回的都是同一個對象,雖然不需要每次都創(chuàng)建新的對象,但無法保證成員變量的線程安全,也就是說在線程池中的執(zhí)行的線程,它們的value值是共享的。而多例模式下,由于每次創(chuàng)建的都是一個新的線程對象,則不存在上述問題。

所以在這里請大家注意無論是我上面的示例代碼還是平常的web開發(fā)中,spirngboot默認(rèn)為單例模式,自定義的成員變量是線程不安全的,需要通過ThreadLocal 或這其他方法做同步處理。

回到我們當(dāng)前的業(yè)務(wù)場景,在這里我們需要每個線程處理的value值不同,互不影響,那么通過@Scope("prototype")注解把TaskThread設(shè)置為多例模式。

總結(jié)

通過上面的示例,我們可以看到springboot與多線程的結(jié)合還是比較簡單,通過配置,我們既可以在spring容器中管理線程類,也可以在線程中使用sping容器中的對象實(shí)例。同時我們在使用的過程當(dāng)中要有意識的去注意線程安全方面的問題和內(nèi)部運(yùn)行機(jī)制的問題。當(dāng)然這里理解的還是比較淺顯,如果有不正確的地方還請大家指出與海涵。

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI