java實現(xiàn)/創(chuàng)建線程的幾種方式小結(jié)
進程與線程
進程可以簡單理解成一個可執(zhí)行程序例如.exe,在Windows中的任務(wù)管理器中可以查看每一個進程,進程是一次程序的執(zhí)行,是程序在數(shù)據(jù)集合上運行的過程,是系統(tǒng)資源調(diào)度的一個單位。進程主要負責向操作系統(tǒng)申請資源。然而一個進程中,多個線程可以共享進程中相同的內(nèi)存或文件資源。線程就是一個進程一個程序要完成所依賴的子任務(wù),這些子任務(wù)便可以看作是一個線程。
第一種方式繼承Thread類
從java源碼可以看出Thread類本質(zhì)上實現(xiàn)了Runnable接口的實例類,代表了線程的一個線程的實例,啟動的線程唯一辦法就是通過Thread類調(diào)用start()方法,start()方法是需要本地操作系統(tǒng)的支持,它將啟動一個新的線程,并且執(zhí)行run()方法。
繼承Thread類實現(xiàn)線程代碼如下
創(chuàng)建一個Thread類,對象直接調(diào)用run方法會出現(xiàn)什么問題?
package cn.thread.線程; public class MyThread extends Thread{ public MyThread(String name){ super(null,null,name); } int piao =10; @Override public void run() { while(piao>0){ System.out.println(Thread.currentThread().getName()+"......"+piao--); } } public static void main(String[] args) { MyThread mt = new MyThread("x"); mt.run(); } }
結(jié)果:
可以發(fā)現(xiàn)是主線程執(zhí)行了run方法,并不是用戶線程執(zhí)行的run方法,此時可以得出用戶線程并沒有啟動,所以并不會執(zhí)行run里面的方法,且執(zhí)行完run方法便結(jié)束線程。
第二種創(chuàng)建線程的方法,實現(xiàn)Runnable接口
相比繼承Thread類而言,實現(xiàn)接口的可擴展性得到了提升,Runnable接口也必須要封裝到Thread類里面,才可以調(diào)用start方法,啟動線程。
實現(xiàn)代碼
package cn.thread.線程; public class MyRunnable implements Runnable{ int piao = 10; @Override public void run() { while(piao>0){ System.out.println(Thread.currentThread().getName()+"-----"+piao--); } } public static void main(String[] args) { Runnable r =new MyRunnable(); Thread t =new Thread(r); t.start(); } }
結(jié)果
第三種創(chuàng)建線程的方法實現(xiàn)Callable接口
Callable接口使用方法和Runnable接口的方法類似不同的一點是Callable接口具有返回值,返回結(jié)果并且可能拋出異常的任務(wù)。實現(xiàn)者定義了一個不帶任何參數(shù)的叫做 call 的方法。 Callable 接口類似于 Runnable,兩者都是為那些其實例可能被另一個線程執(zhí)行的類設(shè)計的。但是 Runnable 不會返回結(jié)果,并且無法拋出經(jīng)過檢查的異常。
實現(xiàn)代碼
對下列代碼進行分析
首先callable是接口不能直接創(chuàng)建對象,也不能創(chuàng)建線程。并且要實現(xiàn)call方法類似run方法的功能,call方法有返回值,會計算結(jié)果,如果無法計算結(jié)果,則拋出一個異常。
執(zhí)行callable任務(wù)之后,可以獲得一個future的對象,future基本上是主線程可以跟蹤進度以及獲取其他線程結(jié)果的一種方式。在這里Test1()方法主要利用線程池和future的方法,去啟動實現(xiàn)Callable接口的線程,具有返回值。而Test2()主要是采用FutureTask類去實現(xiàn)創(chuàng)建一個實現(xiàn)callable接口的線程,futuretask實現(xiàn)了future接口。
package com.openlab.test; import java.util.Random; import java.util.concurrent.Callable; public class CallableTest implements Callable{ @Override public Object call() throws Exception { Random generator = new Random(); Integer randomNumber = generator.nextInt(5); Thread.sleep(randomNumber*1000); return randomNumber; } }
綜合練習代碼:
package cn.thread.線程; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Random; import java.util.concurrent.*; public class CallableTest implements Callable<Object> {//不能直接創(chuàng)建線程 int taskNum; public CallableTest(int taskNum){ this.taskNum = taskNum; } @Override public Object call() throws Exception { System.out.println(">>>"+taskNum+"任務(wù)啟動"); Date dataTemp = new Date(); Thread.sleep(1000); Date dataTemp2 = new Date(); long time = dataTemp2.getTime() - dataTemp.getTime(); System.out.println(">>>>"+taskNum+"任務(wù)終止"); return taskNum+"任務(wù)返回運行結(jié)果"+time; // Random generator = new Random(); // Integer randomNumber = generator.nextInt(5); // Thread.sleep(randomNumber*1000); // return randomNumber; } /*test1方法采用Executors的靜態(tài)方法newFixedThreadPool(taskSize) 創(chuàng)建一個可重用固定線程集合的 線程池,以共享的無界隊列方式來運行這些線程,獲取線程池。ExecutorService的submit方法提交一個 callable實例,得到一個future對象,最終將future對象存儲在list數(shù)組中,加入線程池的過程中就代表 著線程已經(jīng)開始執(zhí)行,相當于一個線程池代理過程,就不需要采用start方法啟動線程。最后對future進行 打印輸出。切記一定要關(guān)閉線程池!*/ static void test1() throws ExecutionException, InterruptedException { System.out.println("程序開始"); Date data1 = new Date(); int taskSize = 5; //構(gòu)建線程池對象 ExecutorService pool = Executors.newFixedThreadPool(taskSize); List<Future> list =new ArrayList<Future>(); for(int i=0;i<taskSize;i++){ Callable c = new CallableTest(i); Future f = pool.submit(c); list.add(pool.submit(c)); } //關(guān)閉線程池 pool.shutdown(); for(Future f:list){ System.out.println(">>>"+f.get().toString()); } Date date2 = new Date(); System.out.println("程序運行結(jié)束-----"+(date2.getTime()-data1.getTime())+"毫秒"); } /*test2方法主要是采用futuretask類,可以直接把callable作為參數(shù)來申明futuretask對象, 這里相當于把線程池換成了futuretask數(shù)組,因為test1線程池可以對callable進行封裝, 在這里可以直接采用futuretask就行封裝,在加上futuretask又實現(xiàn)了runnable接口, 所以可以直接創(chuàng)建線程采用start的方式進行啟動線程。*/ static void test2() throws ExecutionException, InterruptedException { System.out.println("----程序開始-----"); Date date1 =new Date(); int taskSize = 5; FutureTask[] randNumber = new FutureTask[taskSize]; List<Future> list =new ArrayList<Future>(); for(int i=0;i<taskSize;i++){ Callable c = new CallableTest(i); randNumber[i] = new FutureTask(c); Thread t = new Thread(randNumber[i]); t.start(); } for(Future f:randNumber){ System.out.println(">>>"+f.get().toString()); } Date date2 = new Date(); System.out.println("程序運行結(jié)束-----"+(date2.getTime()-date1.getTime())+"毫秒"); } public static void main(String[] args) throws Exception { // CallableTest c = new CallableTest(); // Integer i = (Integer) c.call(); test1(); test2(); } }
執(zhí)行結(jié)果
第四種實現(xiàn)線程的方法,基于線程池
其實在第三種的方法中就提到了兩種實現(xiàn)方法,一種線程池+future,另一種futuretask的方法。線程和數(shù)據(jù)庫連接這些資源都是非常寶貴的資源。那么每次需要的時候創(chuàng)建,不需要的時候銷毀,是非常浪費資源的。那么我們就可以使用緩存的策略,也就是使用線程池。
// 創(chuàng)建線程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); while(true) { threadPool.execute(new Runnable() { // 提交多個線程任務(wù),并執(zhí)行 @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running .."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } }
總結(jié)
理論上實現(xiàn)線程的方法還有一些,本文所提及到的,基本都是一些創(chuàng)建線程常用的方法。希望本文對大家在學習線程的過程中有所幫助。
相關(guān)文章
Java以編程方式實現(xiàn)JAR文件的創(chuàng)建
在這篇文章中,我們將為大家詳細介紹一下利用Java語言以編程方式創(chuàng)建jar文件的過程。文中的示例代碼講解詳細,感興趣的可以了解一下2022-07-07使用HttpSessionListener監(jiān)聽器實戰(zhàn)
這篇文章主要介紹了使用HttpSessionListener監(jiān)聽器實戰(zhàn),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03使用logstash同步mysql數(shù)據(jù)到elasticsearch實現(xiàn)
這篇文章主要為大家介紹了使用logstash同步mysql數(shù)據(jù)到elasticsearch實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12如何解決使用restTemplate進行feign調(diào)用new HttpEntity<>報錯問題
這篇文章主要介紹了如何解決使用restTemplate進行feign調(diào)用new HttpEntity<>報錯問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06