您好,登錄后才能下訂單哦!
作者:唐彤
簡介
創建線程,是多線程編程中最基本的操作,彤哥總結了一下,大概有8種創建線程的方式,你知道嗎?
1.繼承Thread類并重寫run()方法
public?class?CreatingThread01?extends?Thread?{ ?@Override ?public?void?run()?{ ?System.out.println(getName()?+?"?is?running"); ?} ?public?static?void?main(String[]?args)?{ ?new?CreatingThread01().start(); ?new?CreatingThread01().start(); ?new?CreatingThread01().start(); ?new?CreatingThread01().start(); ?} }
繼承Thread類并重寫run()方法,這種方式的弊端是一個類只能繼承一個父類,如果這個類本身已經繼承了其它類,就不能使用這種方式了。
2.實現Runnable接口
public?class?CreatingThread02?implements?Runnable?{ ?@Override ?public?void?run()?{ ?System.out.println(Thread.currentThread().getName()?+?"?is?running"); ?} ?public?static?void?main(String[]?args)?{ ?new?Thread(new?CreatingThread02()).start(); ?new?Thread(new?CreatingThread02()).start(); ?new?Thread(new?CreatingThread02()).start(); ?new?Thread(new?CreatingThread02()).start(); ?} }
實現Runnable接口,這種方式的好處是一個類可以實現多個接口,不影響其繼承體系。
3.匿名內部類
public?class?CreatingThread03?{ ?public?static?void?main(String[]?args)?{ ?//?Thread匿名類,重寫Thread的run()方法 ?new?Thread()?{ ?@Override ?public?void?run()?{ ?System.out.println(getName()?+?"?is?running"); ?} ?}.start(); ?//?Runnable匿名類,實現其run()方法 ?new?Thread(new?Runnable()?{ ?@Override ?public?void?run()?{ ?System.out.println(Thread.currentThread().getName()?+?"?is?running"); ?} ?}).start(); ?//?同上,使用lambda表達式函數式編程 ?new?Thread(()->{ ?System.out.println(Thread.currentThread().getName()?+?"?is?running"); ?}).start(); ?} }
使用匿名類的方式,一是重寫Thread的run()方法,二是傳入Runnable的匿名類,三是使用lambda方式,現在一般使用第三種(java8+),簡單快捷。
4.實現Callabe接口
public?class?CreatingThread04?implements?Callable<Long>?{ ?@Override ?public?Long?call()?throws?Exception?{ ?Thread.sleep(2000); ?System.out.println(Thread.currentThread().getId()?+?"?is?running"); ?return?Thread.currentThread().getId(); ?} ?public?static?void?main(String[]?args)?throws?ExecutionException,?InterruptedException?{ ?FutureTask<Long>?task?=?new?FutureTask<>(new?CreatingThread04()); ?new?Thread(task).start(); ?System.out.println("等待完成任務"); ?Long?result?=?task.get(); ?System.out.println("任務結果:"?+?result); ?} }
實現Callabe接口,可以獲取線程執行的結果,FutureTask實際上實現了Runnable接口。
5.定時器(java.util.Timer)
public?class?CreatingThread05?{ ?public?static?void?main(String[]?args)?{ ?Timer?timer?=?new?Timer(); ?//?每隔1秒執行一次 ????????timer.schedule(new?TimerTask()?{ ?@Override ?public?void?run()?{ ?System.out.println(Thread.currentThread().getName()?+?"?is?running"); ?} ?},?0?,?1000); ?} }
使用定時器java.util.Timer可以快速地實現定時任務,TimerTask實際上實現了Runnable接口。
6.線程池
public?class?CreatingThread06?{ ?public?static?void?main(String[]?args)?{ ?ExecutorService?threadPool?=?Executors.newFixedThreadPool(5); ?for?(int?i?=?0;?i?<?100;?i++)?{ ????????????threadPool.execute(()->?System.out.println(Thread.currentThread().getName()?+?"?is?running")); ?} ?} }
使用線程池的方式,可以復用線程,節約系統資源。
7.并行計算(Java8+)
public?class?CreatingThread07?{ ?public?static?void?main(String[]?args)?{ ?List<Integer>?list?=?Arrays.asList(1,?2,?3,?4,?5); ?//?串行,打印結果為12345 ????????list.stream().forEach(System.out::print); ?System.out.println(); ?//?并行,打印結果隨機,比如35214 ????????list.parallelStream().forEach(System.out::print); ?} }
使用并行計算的方式,可以提高程序運行的效率,多線程并行執行。
8.Spring異步方法
首先,springboot啟動類加上 @EnableAsync注解(@EnableAsync是spring支持的,這里方便舉例使用springboot)。
@SpringBootApplication @EnableAsync public?class?Application?{ ?public?static?void?main(String[]?args)?{ ?SpringApplication.run(Application.class,?args); ?} }
其次,方法加上 @Async注解。
@Service public?class?CreatingThread08Service?{ ?@Async ?public?void?call()?{ ?System.out.println(Thread.currentThread().getName()?+?"?is?running"); ?} }
然后,測試用例直接跟使用一般的Service方法一模一樣。
@RunWith(SpringRunner.class) @SpringBootTest(classes?=?Application.class) public?class?CreatingThread08Test?{ ?@Autowired ?private?CreatingThread08Service?creatingThread08Service; ?@Test ?public?void?test()?{ ????????creatingThread08Service.call(); ????????creatingThread08Service.call(); ????????creatingThread08Service.call(); ????????creatingThread08Service.call(); ?} }
運行結果如下:
task-3?is?running task-2?is?running task-1?is?running task-4?is?running
可以看到每次執行方法時使用的線程都不一樣。
使用Spring異步方法的方式,可以說是相當地方便,適用于前后邏輯不相關聯的適合用異步調用的一些方法,比如發送短信的功能。
歡迎大家關注我的公種浩【程序員追風】,文章都會在里面更新,整理的資料也會放在里面。
總結
(1)繼承Thread類并重寫run()方法;
(2)實現Runnable接口;
(3)匿名內部類;
(4)實現Callabe接口;
(5)定時器(java.util.Timer);
(6)線程池;
(7)并行計算(Java8+);
(8)Spring異步方法;
福利
上面介紹了那么多創建線程的方式,其實本質上就兩種,一種是繼承Thread類并重寫其run()方法,一種是實現Runnable接口的run()方法,那么它們之間到底有什么聯系呢?
請看下面的例子,同時繼承Thread并實現Runnable接口,應該輸出什么呢?
public?class?CreatingThread09?{ ?public?static?void?main(String[]?args)?{ ?new?Thread(()->?{ ?System.out.println("Runnable:?"?+?Thread.currentThread().getName()); ?})?{ ?@Override ?public?void?run()?{ ?System.out.println("Thread:?"?+?getName()); ?} ?}.start(); ?} }
說到這里,我們有必要看一下Thread類的源碼:
public?class?Thread?implements?Runnable?{ ?//?Thread維護了一個Runnable的實例 ?private?Runnable?target; ?public?Thread()?{ ????????init(null,?null,?"Thread-"?+?nextThreadNum(),?0); ?} ?public?Thread(Runnable?target)?{ ????????init(null,?target,?"Thread-"?+?nextThreadNum(),?0); ?} ?private?void?init(ThreadGroup?g,?Runnable?target,?String?name, ?long?stackSize,?AccessControlContext?acc, ?boolean?inheritThreadLocals)?{ ?//?... ?//?構造方法傳進來的Runnable會賦值給target ?this.target?=?target; ?//?... ?} ?@Override ?public?void?run()?{ ?//?Thread默認的run()方法,如果target不為空,會執行target的run()方法 ?if?(target?!=?null)?{ ????????????target.run(); ?} ?} }
看到這里是不是豁然開朗呢?既然上面的例子同時繼承Thread并實現了Runnable接口,根據源碼,實際上相當于重寫了Thread的run()方法,在Thread的run()方法時實際上跟target都沒有關系了。
所以,上面的例子輸出結果為 Thread:Thread-0,只輸出重寫Thread的run()方法中的內容。
最后
歡迎大家一起交流,喜歡文章記得點個贊喲,感謝支持!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。