建立並啓動線程的6種方式java
1.1繼承Thread類方式建立線程的實現步驟:spring
步驟:數據庫
1) 定義一個類A繼承於java.lang.Thread類設計模式
2)在A類中覆蓋Thread類中的run方法緩存
3)咱們在run方法中編寫須要執行的操做---->run方法裏的,線程執行體網絡
4)在main方法(線程)中,建立線程對象,並啓動線程數據結構
注意:千萬不要調用run方法,若是調用run方法比如是對象調用方法,依然仍是隻有一個線程,並無開啓新的線程。多線程
1.2需求:使用兩個線程實現邊聽歌邊打遊戲框架
實現代碼:異步
//音樂線程 public class MusicThread { public static void main(String[] args) { //建立遊戲線程對象 GameThread game = new GameThread(); //啓動遊戲線程 game.start(); while(true){ System.out.println(Thread.currentThread().getName()+"聽音樂!"); } } } //遊戲線程 class GameThread extends Thread{ @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName()+"打遊戲!"); } } }
注意:有的小夥伴可能以爲音樂線程沒有啓動,在這裏其實音樂線程已經啓動起來了,而啓動音樂線程的對象就是咱們的JVM,此處main方法其實啓動的時候會建立一個主線程去執行main方法,因此我在這裏使用主線程做爲了個人音樂線程。
2.1實現Runnable接口方式建立線程的實現步驟:
1)定義一個類A實現於java.lang.Runnable接口,注意A類不是線程類。
2)在A類中覆蓋Runnable接口中的run方法。
3) 咱們在run方法中編寫須要執行的操做---->run方法裏的,線程執行體。
4)在main方法(線程)中,建立線程對象,並啓動線程。
2.2需求:使用兩個線程實現邊聽歌邊打遊戲
實現代碼:
//音樂線程 public class MusicThread { public static void main(String[] args) { //建立遊戲線程對象 Thread game = new Thread(new Game()); //啓動遊戲線程 game.start(); while(true){ System.out.println(Thread.currentThread().getName()+"聽音樂!"); } } } //遊戲 class Game implements Runnable{ @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName()+"打遊戲!"); } } }
2.3 繼承方式和實現方式的區別
1)繼承方式是一個類繼承了Thread後成爲線程類的子類,實現方式是一個類實現Runnable接口,可是這個類不是線程類,由於該類沒有start等方法。
2)啓動的時候繼承方式直接調用本身的start方法,實現方式是藉助了Thread中的start方法啓動的,自身沒有start方法。
3)繼承方式調用的run方法是經過方法覆蓋,經過繼承方式實現的,運行的時候先找子類,沒有最後才運行父類的run方法。實現方式是執行Thread的run方法,而Thread中的run方法調用了實現類中的run方法,使用過組合關係的方法調用實現的。
3.1使用Callable和FutureTask建立線程的實現步驟:
1)定義一個Callable接口的實現類
2)建立Callable實現類對象傳遞給FutureTask構造器
3)將FutureTask對象傳遞給Thread構造器
4)Thread對象調用start方法啓動線程
5)經過FutureTask對象的get方法獲取線程運行的結果
注意:
Future就是對於具體的Runnable或者Callable任務的執行結果進行取消、查詢是否完成、獲取結果。必要時能夠經過get方法獲取執行結果,該方法會阻塞直到任務返回結果。
使用場景:使用多線程計算結果並返回該結果。
3.2需求:使用2個線程異步計算1-1000,000內之和
實現代碼:
public class CallableDemo { public static void main(String[] args) throws Exception { //1.建立並啓動線程 Callable<Integer> call1 = new CallableImpl(0, 50000); Callable<Integer> call2 = new CallableImpl(50001, 100000); FutureTask<Integer> f1 = new FutureTask<>(call1); FutureTask<Integer> f2 = new FutureTask<>(call2); new Thread(f1).start(); new Thread(f2).start(); //2.獲取每個線程的結果 int ret1 = f1.get(); int ret2 = f2.get(); int ret= ret1+ret2; System.out.println(ret); } } class CallableImpl implements Callable<Integer>{ private int min; private int max; public CallableImpl(int min, int max) { this.min = min; this.max = max; } @Override public Integer call() throws Exception { int sum = 0; for (int i = min; i <= max; i++) { sum+=i; } return sum; } }
3.3Callable和Runnable的區別以下:
Callable定義的方法是call,而Runnable定義的方法是run。
Callable的call方法能夠有返回值,而Runnable的run方法不能有返回值。
Callable的call方法可拋出異常,而Runnable的run方法不能拋出異常。
注意:
FutureTask爲Runnable的實現類
FutureTask能夠視爲一個閉鎖(門閂),由於只有當線程運行完纔會出現結果。
線程池,顧名思義就是一個池子裏面放了不少的線程,咱們用就將線程從裏面拿出來,使用完畢就放回去池子中。設計和數據庫鏈接池類似,存在靜態工廠方法用於建立各類線程池。
操做步驟:
1)使用Executors工具類中的靜態工廠方法用於建立線程池
newFixedThreadPool:建立可重用且固定線程數的線程池,
newScheduledThreadPool:建立一個可延遲執行或按期執行的線程池
newCachedThreadPool:建立可緩存的線程池
2)使用execute方法啓動線程
3)使用shutdown方法等待提交的任務執行完成並後關閉線程。
代碼演示以下:
public class Demo4 { public static void main(String[] args) { Executor executor = Executors.newFixedThreadPool(5); executor.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); ((ExecutorService) executor).shutdown(); } }
在Spring3以後,Spring引入了對多線程的支持,若是你使用的版本在3.1之前,應該仍是須要經過傳統的方式來實現多線程的。從Spring3同時也是新增了Java的配置方式,並且Java配置方式也逐漸成爲主流的Spring的配置方式。
代碼演示以下:
導入的包:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.1.0.RELEASE</version> </dependency> </dependencies>
配置類:
@Configuration @ComponentScan("cn.wolfcode") @EnableAsync //容許使用異步任務 public class SpringConfig {}
服務類:
@Service public class SpringService { @Async // 這裏進行標註爲異步任務,在執行此方法的時候,會單獨開啓線程來執行 public void dowork1() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "-->" + i); } } @Async public void dowork2() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "-->" + i); } } }
測試類:
public class SpringThreadDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); SpringService bean = context.getBean(SpringService.class); bean.dowork1(); bean.dowork2(); } }
注意:此時會出現一個DEBUG信息
在這裏DEBUG信息不是什麼錯誤,不會影響代碼的正常運行,其實能夠不用管的,可是爲何出現這個問題呢?
Spring的定時任務調度器會經過BeanFactory.getBean的方法來嘗試獲取一個註冊過的TaskExecutor對象來作任務調度,獲取不到TaskExecutor對象再嘗試找ScheduledExecutorService 對象,都找不到就報DEBUG信息。報錯以後就找本身自己默認的scheduler定時器對象,這個舉動實際上是作一個提醒做用,因此若是沒有強迫症能夠不用管它。
解決Spring使用多線程的報錯信息
強迫症患者想要解決怎麼辦,三種方式:
嚴格來講定時器(Timer)不是線程,他只是調度線程的一種工具,它裏面封裝了一個線程,因此咱們可使用定時器來使用線程。
操做步驟:
1)建立Timer 對象
2)調用schedule方法
3)傳入TimerTask子類對象
代碼演示以下:
Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "-->" + i); } } }, 100, 100);
文源網絡,僅供學習之用,若有侵權,聯繫刪除。我將優質的技術文章和經驗總結都聚集在了個人公衆號【Java圈子】裏。
爲方便你們學習,我還整理了一套學習資料,涵蓋Java虛擬機、spring框架、Java線程、數據結構、設計模式等等,免費提供給熱愛Java的同窗~