Java多線程run方法中直接調用service業務類應註意的問題及解決
多線程run方法中直接調用service業務類應註意
Java多線程run方法裡邊使用service業務類會產生java.lang.NullPointerException異常的問題,這是由於spring註入的業務類為null,或者直接new的業務對象也為null。
多線程為瞭線程安全會防止註入,因此在想使用service業務類時,需要使用ApplicationContext的方式獲取bean的方法獲取service類。
獲取ApplicationContext的類要實現ApplicationContextAware接口,如下:
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class ApplicationContextUtil implements ApplicationContextAware { private static ApplicationContext context; public void setApplicationContext(ApplicationContext context) throws BeansException { this.context = context; } public static ApplicationContext getContext() { return context; } }
然後在run方法裡使用以上方法創建業務對象,如下:
XXXServiceI xxxService = ApplicationContextUtil.getContext.getBean(XXXServiceI.class);
這樣就能正常使用該業務類瞭。
圖解如下
多線程知識點
線程啟動的四種方式
1.、繼承Thread類重寫Thread的run方法,在run方法中進行操作,用start方法啟動線程
2、繼承Runnable接口,實現run方法,在run方法中進行操作,需要傳入當前類的實例對象創建一個Thread實例,然後調用start方法啟動線程
3、實現Callable接口,重寫call()方法,需要註意的是,前兩種方法都是不需要響應的,直接就執行瞭,但是實現Callable接口,重寫call()方法則是需要等待線程響應的,所以雖然啟動瞭其他線程,但是卻是一個線程在執行,並不能算標準的多線程。
4、線程池
使用@Aysnc註解實現多線程
同一個類中,方法A 引用方法B 方法B加異步@Async註解 不會有效
被加@Async方法和調用方 不能再同一個類中
用戶線程與守護線程的區別
Java內創建的線程默認是創建用戶線程,比如new Thread(線程對象).start
Thread thread = new Thread(); // 默認為false,都是用戶線程 thread.setDaemon(true); // 表示設置為守護線程 thread.setDaemon(false); // 表示設置為用戶線程
- 用戶線程:不zhi隨著其他線程的死亡而死亡,隻有兩種情況dao死掉,一是在運行中出現異常而終止,二是正常把程序執行完畢,線程死亡
- 守護線程:隨著用戶線程的死亡而死亡,當用戶線程死完瞭守護線程也死瞭,比如gc垃圾回收線程。用戶線程存在,那gc就有活著的必要,反之就沒用瞭。
線程的六種狀態
1. New:初始狀態,線程被創建,沒有調用start()
2. Runnable:運行狀態,Java線程把操作系統中的就緒和運行兩種狀態統一稱為“運行中”
3. Blocked:阻塞,線程進入等待狀態,線程因為某種原因,放棄瞭CPU的使用權
- 阻塞的幾種情況:
- A. 等待阻塞:運行的線程執行瞭wait(),JVM會把當前線程放入等待隊列
- B. 同步阻塞:運行的線程在獲取對象的同步鎖時,如果該同步鎖被其他線程占用瞭,JVM會把當前線程放入鎖池中
- C. 其他阻塞:運行的線程執行sleep(),join()或者發出IO請求時,JVM會把當前線程設置為阻塞狀態,當sleep()執行完,join()線程終止,IO處理完畢線程再次恢復
4. Waiting:等待狀態
5. timed_waiting:超時等待狀態,超時以後自動返回
6. terminated:終止狀態,當前線程執行完畢
Java鎖的可重入性
java鎖的可重入性機制可以解決下面這個問題,直接上代碼:
public class Demo1 { public synchronized void functionA(){ System.out.println("iAmFunctionA"); functionB(); } public synchronized void functionB(){ System.out.println("iAmFunctionB"); }
假設Java沒有提供synchronized 強制原子性的內部鎖機制:functionA()和functionB()都是同步方法,當線程進入funcitonA()會獲得該類的對象鎖,這個鎖"new Demo1()",在functionA()對方法functionB()做瞭調用,但是functionB()也是同步的,因此該線程需要再次獲得該對象鎖(new Demo1()),但是JVM會認為這個線程已經獲取瞭此對象的鎖,而不能再次獲取,從而無法調用functionB()方法,從而造成死鎖。
線程池的四種拒絕策略
當線程池的任務緩存隊列已滿並且線程池中的線程數目達到maximumPoolSize時,如果還有任務到來就會采取任務拒絕策略,通常有以下四種策略:
ThreadPoolExecutor.AbortPolicy
:丟棄任務並拋出RejectedExecutionException異常。
ThreadPoolExecutor.DiscardPolicy
:丟棄任務,但是不拋出異常。
ThreadPoolExecutor.DiscardOldestPolicy
:丟棄隊列最前面的任務,然後重新提交被拒絕的任務
ThreadPoolExecutor.CallerRunsPolicy
:由調用線程(提交任務的線程)處理該任務
sleep和wait的區別
- sleep是線程中的方法,但是wait是Object中的方法
- sleep方法不會釋放lock,但是wait會釋放,而且會加入到等待隊列中
- sleep不需要被喚醒,但是wait需要
為什麼wait(),notify(),notifyAll()在對象中,而不在Thread類中
java中鎖的級別是對象級而不是線程級,每個對象都有鎖,通過線程獲得。如果wait()方法在線程中,線程正在等待的是哪個鎖就不明顯瞭。
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Java多線程Thread類的使用及註意事項
- SpringBoot中的main方法註入service
- springboot實現在工具類(util)中調用註入service層方法
- 新手初學Java網絡編程
- 分析java並發中的wait notify notifyAll