首頁 >Java >java教程 >springboot下多執行緒開發時需要注意的地方有哪些? (總結)

springboot下多執行緒開發時需要注意的地方有哪些? (總結)

不言
不言原創
2018-09-25 15:44:415105瀏覽

這篇文章帶給大家的內容是關於springboot下多執行緒開發時需要注意的地方有哪些? (總結),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

基於springboot的多執行緒程式開發過程中,由於本身也需要注入spring容器進行管理,才能發揮springboot的優勢。所以這篇文字主要用來記錄開發中兩者結合時需要注意的一些事項。

第一步我們把線程類別的實例注入sping容器進行管理

@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);
        //这里主要保存上下文对象实例,需要加上。SpringBootUtils类网上很多,可以自己搜下
        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容器當中.

然後是線程的啟動,這裡在我的業務場景中有兩種情況:

##1 、程式執行時,自動啟動;

這在一般的可執行程式裡面,當然可以直接在main函數裡執行透過程式碼啟動執行緒。但在springboot中,我們可以使用@PostConstruct註解的方式,讓已經註入bean容器的線程物件自啟動

@Component
public class  demoThread extends Thread
{
    //注意这里,如果你没有实现把多线程类的实例注入到spring容器中,这里你是无法拿到其他自动装配的对象实例的的,这也是我们第一步的意义所在。
    @Autowired
    private XxxService xxxService;

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

    public void run() {
        // Ok,在这里你就可以实现线程要实现的功能逻辑了,自然也可以直接使用装配好的sevice对象实例。
        
    }
}

 2、在程式中,需要開啟線程時啟動,例如在從kafka接收數據,開啟線程處理,當然這種情況下也需要通過第一步,把線程類實例注入到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获取线程类的实例
           thread = SpringBootUtils.getBean(TaskThread.class);
           //启动线程
           //new Thread(thread).start() ; 
           //向线程对象里传值
           thread.init(i);
           //放入线程池执行
           taskPool.execute(thread);

    }

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

    @Autowired
    private XxxService xxxService;
    
    //ThreadLocal  对象,单例模式下可以保证成员变量的线程安全和独立性。
    public ThreadLocalc0f559cc8d56b43654fcbe4aa9df7b4a valueLocal =  new ThreadLocal 46fc8f240055341038a5bc0c386009d9 () {
        @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")註解設定為多例模式還是預設單例模式。

在單例模式下SpringBootUtils.getBean(TaskThread.class) 每次回傳的都是同一個對象,雖然不需要每次都建立新的對象,但無法保證成員變數的線程安全,也就是說在線程池中的執行的線程,它們的value值是共享的。

而在多例模式下,由於每次創建的都是一個新的線程對象,則不存在上述問題。

所以在這裡請大家注意無論是我上面的範例程式碼還是平常的web開發中,spirngboot預設為單例模式,自訂的成員變數是執行緒不安全的,需要透過ThreadLocal 或這其他方法做同步處理。

回到我們目前的業務場景,在這裡我們需要每個執行緒處理的value值不同,互不影響,那麼透過@Scope("prototype")註解把TaskThread設定為多例模式。

總結

透過上面的範例,我們可以看到springboot與多線程的結合還是比較簡單,透過配置,我們既可以在spring容器中管理線程類,也可以在線程中使用sping容器中的物件實例。同時我們在使用的過程當中要有意識的去注意線程安全方面的問題和內部運行機制的問題。當然這裡理解的還是比較淺顯,如果有不正確的地方還請大家指出與海涵。

以上是springboot下多執行緒開發時需要注意的地方有哪些? (總結)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn