Java線程創(chuàng)建的四種方式總結(jié)
多線程的創(chuàng)建,方式一:繼承于Thread類
1.創(chuàng)建一個繼承于Thread類的子類
2.重寫Thread類的run()--->將此線程執(zhí)行的操作聲明在run()中
3.創(chuàng)建Thread類的子類的對象
4.通過此對象調(diào)用start():
start()方法的兩個作用:
A.啟動當(dāng)前線程
B.調(diào)用當(dāng)前線程的run()
創(chuàng)建過程中的兩個問題:
問題一:我們不能通過直接調(diào)用run()的方式啟動線程
問題二:在啟動一個線程,遍歷偶數(shù),不可以讓已經(jīng)start()的線程去執(zhí)行,會報異常;正確的方式是重新創(chuàng)建一個線程的對象。
//1.創(chuàng)建一個繼承于Thread類的子類 class MyThread extends Thread{ //2.重寫Thread類的run() @Override public void run() {//第二個線程 for(int i = 0;i < 10;i++){ if(i % 2 == 0){ System.out.println(i); } } } } public class ThreadTest { public static void main(String[] args) {//主線程 //3.創(chuàng)建Thread類的子類的對象 MyThread t1 = new MyThread(); //4.通過此對象調(diào)用start() t1.start(); //問題一:不能通過直接調(diào)用run()的方式啟動線程 // t1.run();//錯誤的 //問題二:再啟動一個線程:我們需要再創(chuàng)建 一個對象 //t1.start();//錯誤的 MyThread t2 = new MyThread(); t2.start(); for(int i = 0;i < 10;i++){ if(i % 2 != 0){ System.out.println(i + "****main()******"); } } } }
此代碼在主線程內(nèi)輸出奇數(shù),在另一個線程里輸出偶數(shù),則輸出結(jié)果應(yīng)該是兩個輸出結(jié)果是交互的。
1****main()******
3****main()******
5****main()******
7****main()******
0
2
4
6
8
9****main()******
class Window extends Thread{//創(chuàng)建三個窗口賣票, 總票數(shù)為100張,使用繼承于Thread類的方式 private static int ticket = 100;//三個窗口共享:聲明為static @Override public void run() { while(true){ if(ticket > 0){ System.out.println(getName() + ":賣票,票號為:" + ticket); ticket--; }else{ break; } } } } public class WindowTest2 { public static void main(String[] args) { Window t1 = new Window(); Window t2 = new Window(); Window t3 = new Window(); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } }
public class ThreadDemo { public static void main(String[] args) { // MyThread1 m1 = new MyThread1(); // MyThread2 m2 = new MyThread2(); // m1.start(); // m2.start(); //由于造的類只創(chuàng)建過一次對象,后面就不用了,可以考慮使用匿名類的方式 //創(chuàng)建Thread類的匿名子類的方式 new Thread(){ @Override public void run() { for(int i = 0;i < 100;i++){ if(i % 2 == 0){ System.out.println(i); } } } }.start(); new Thread(){ @Override public void run() { for(int i = 0;i < 100;i++){ if(i % 2 != 0){ System.out.println(i); } } } }.start(); } } class MyThread1 extends Thread{ @Override public void run() { for(int i = 0;i < 100;i++){ if(i % 2 == 0){ System.out.println(i); } } } } class MyThread2 extends Thread{ @Override public void run() { for(int i = 0;i < 100;i++){ if(i % 2 != 0){ System.out.println(i); } } } }
創(chuàng)建多線程的方式二:實現(xiàn)Runnable接口
- 創(chuàng)建一個實現(xiàn)了Runnable接口的類
- 實現(xiàn)類去實現(xiàn)Runnable中的抽象方法:run()
- 創(chuàng)建實現(xiàn)類的對象
- 將此對象作為參數(shù)傳遞到Thread類的構(gòu)造器中,創(chuàng)建Thread類的對象
- 通過Thread類的對象調(diào)用start()
class MThread implements Runnable{ //2.實現(xiàn)類去實現(xiàn)Runnable中的抽象方法:run() @Override public void run() { for(int i = 0;i < 100;i++){ if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } } public class ThreadTest1 { public static void main(String[] args) { //3.創(chuàng)建實現(xiàn)類的對象 MThread mThread = new MThread(); //4.將此對象作為參數(shù)傳遞到Thread類的構(gòu)造器中,創(chuàng)建Thread類的對象 Thread t1 = new Thread(mThread); t1.setName("線程1"); //5.通過Thread類的對象調(diào)用start():A.啟動線程B.調(diào)用當(dāng)前線程的run()-->調(diào)用了Runnable類型的target t1.start(); //再啟動一個線程,遍歷100以內(nèi)的偶數(shù)//只需重新實現(xiàn)步驟4,5即可 Thread t2 = new Thread(mThread); t2.setName("線程2"); t2.start(); } }
class window1 implements Runnable{//創(chuàng)建三個窗口賣票, 總票數(shù)為100張,使用實現(xiàn)Runnable接口的方式 private int ticket = 100; Object obj = new Object(); @Override public void run() { while (true){ if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "賣票,票號為:" + ticket); ticket--; } else { break; } } } } public class WindowTest { public static void main(String[] args) { window1 w = new window1();//只造了一個對象,所以100張票共享 Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("線程1"); t2.setName("線程2"); t3.setName("線程3"); t1.start(); t2.start(); t3.start(); } }
創(chuàng)建線程的方式三:實現(xiàn)Callable接口---JDK5.0新增
與使用Runnable相比,Callable功能更強大些
>相比run()方法,可以有返回值
>方法可以拋出異常
>支持泛型的返回值
>需要借助FutureTask類,比如獲取返回結(jié)果
Future接口
>可以對具體Runnable、Callable任務(wù)的執(zhí)行結(jié)果進行取消、查詢是否完成、獲取結(jié)果等。
>FutureTask是Futrue接口的唯一的實現(xiàn)類
>FutureTaskb同時實現(xiàn)了Runnable,Future接口。它既可以作為Runnable被線程執(zhí)行,又可以作為Future得到Callable的返回值
//1.創(chuàng)建一個實現(xiàn)Callable的實現(xiàn)類 class NumThread implements Callable{ //2.實現(xiàn)call方法,將此線程需要執(zhí)行的操作聲明在call()中 @Override public Object call() throws Exception { int sum = 0; for(int i = 1;i <= 100;i++){ if(i % 2 == 0){ System.out.println(i); sum += i; } } return sum;//sum是int,自動裝箱為Integer(Object的子類) } } public class ThreadNew { public static void main(String[] args) { //3.創(chuàng)建Callable接口實現(xiàn)類的對象 NumThread numThread = new NumThread(); //4.將此Callable接口實現(xiàn)類的對象作為參數(shù)傳遞到 FutureTask的構(gòu)造器中,創(chuàng)建FutureTask的對象 FutureTask futureTask = new FutureTask(numThread); //5.將 FutureTask的對象作為參數(shù)傳遞到Thread類的構(gòu)造器中,創(chuàng)建Thread對象,并調(diào)用start() new Thread(futureTask).start(); try { //獲取Callable中call()的返回值(不是必須的步驟) //get()返回值即為FutureTask構(gòu)造器參數(shù)Callable實現(xiàn)類重寫的call()的返回值。 Object sum = futureTask.get(); System.out.println("總和為:" + sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
創(chuàng)建線程的方式四:使用線程池--->JDK5.0新增
背景:經(jīng)常創(chuàng)建和銷毀、使用量特別大的資源,比如并發(fā)情況下的線程,對性能影響很大。
思路:提前創(chuàng)建好多個線程,放入線程池中,使用時直接獲取,使用完放回池中??梢员苊忸l繁創(chuàng)建銷毀、實現(xiàn)重復(fù)利用。類似生活中的公共交通工具。
好處:>提高響應(yīng)速度(減少了創(chuàng)建新線程的時間)
>降低資源消耗(重復(fù)利用線程池中線程,不需要每次都創(chuàng)建)
>便于線程管理:A.corePoolSize:核心池的大小 B.maximumPoolSize:最大線程數(shù) C.keepAliveTime:線程沒有任務(wù)時最多保持多長時間后會終止
class NumberThread implements Runnable{ @Override public void run() { for(int i = 0;i <= 100;i++){ if(i % 2 == 0){ System.out.println(Thread.currentThread().getName() + ":" + i); } } } } class NumberThread1 implements Runnable{ @Override public void run() { for(int i = 0;i <= 100;i++){ if(i % 2 != 0){ System.out.println(Thread.currentThread().getName() + ":" + i); } } } } public class ThreadPool { public static void main(String[] args) { //1.提供指定線程數(shù)量的線程池 ExecutorService service = Executors.newFixedThreadPool(10); ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; //設(shè)置線程池的屬性 // System.out.println(service.getClass()); // service1.setCorePoolSize(15); // service1.setKeepAliveTime(); //2.執(zhí)行指定的線程操作。需要提供實現(xiàn)Runnable 接口或Callable接口實現(xiàn)類的對象 service.execute(new NumberThread());//適用于Runnable service.execute(new NumberThread1());//適用于Runnable // service.submit(Callable callable);//適用于Callable //3.關(guān)閉連接池 service.shutdown(); } }
比較創(chuàng)建線程的兩種方式:
開發(fā)中:優(yōu)先選擇:實現(xiàn)Runnable接口的方式
原因:1.實現(xiàn)的方式?jīng)]有類的單繼承性的局限性
2.實現(xiàn)的方式更適合來處理多個線程有共享數(shù)據(jù)的情況。
系:public class Thread implements Runnable
相同點:兩種方式都需要重寫run(),將線程要執(zhí)行的邏輯聲明在run()中
程序(program)是為完成特定任務(wù)、用某種語言編寫的一組指令的集合。即指一段靜態(tài)的代碼,靜態(tài)對象。
進程(process)是程序的一次執(zhí)行過程,或是正在運行的一個程序。是一個動態(tài)的過程:有它自身的產(chǎn)生、存在和消亡的過程。---生命周期
線程(thread),進程可進一步細(xì)化為線程,是一個程序內(nèi)部的一條執(zhí)行路徑。
線程作為調(diào)度和執(zhí)行的單位,每個線程擁有獨立的運行棧和計數(shù)器,每個進程擁有獨立的方法區(qū)和堆;意味著,多個線程共享一個方法區(qū)和堆。而共享的就可以優(yōu)化,同時,共享的也會帶來安全隱患,這就需要我們解決線程安全問題
背景:以單核CPU為例,只使用單個線程先后完成多個任務(wù)(調(diào)用多個方法),肯定比用多個線程來完成用的時間更短,為何仍需使用多線程呢?
使用多線程的優(yōu)點:
1.提高應(yīng)用程序的響應(yīng)。對圖形化界面更有意義,可增強用戶體驗。
2.提高計算機系統(tǒng)CPU的利用率
3.改善程序結(jié)構(gòu)。將即長又復(fù)雜的線程分為多個線程,獨立運行,利于理解和修改
何時需要多線程
1.程序需要同時執(zhí)行兩個或多個任務(wù)。
2.程序需要實現(xiàn)一些需要等待的任務(wù)時,如用戶輸入、文件讀寫操作、網(wǎng)絡(luò)操作、搜索等。
3.需要一些后臺運行的程序時。
public class Sample{ public void method1(String str){ System.out.println(str); } public void method2(String str){ method1(str); } public static void main(String[] args){ Sample s = new Sample(); s.method2("hello!"); } }
注意:此程序不是多線程!main方法中調(diào)用了method1,method1中又調(diào)用了method2;是一條執(zhí)行路徑,所以是單線程
測試Thread類的常用方法:
1.start():啟動當(dāng)前線程:調(diào)用當(dāng)前線程的run()
2.run():通常需要重寫Thread類中的此方法,將創(chuàng)建的線程要執(zhí)行的操作聲明在此方法中
3.currentThread():靜態(tài)方法,返回執(zhí)行當(dāng)前代碼的線程
4.getName():獲取當(dāng)前線程的名字
5.setName():設(shè)置當(dāng)前線程的名字
6.yield():釋放當(dāng)前CPU的執(zhí)行權(quán)(下一刻CPU執(zhí)行的線程仍是隨機的)
>暫停當(dāng)前正在執(zhí)行的線程,把執(zhí)行機會讓給優(yōu)先級相同或更高的線程
>若隊列中沒有同優(yōu)先級的線程,忽略此方法
7.join():在線程a中調(diào)用線程b的join(),此時,線程a就進入阻塞狀態(tài)(停止執(zhí)行),直到線程b完全執(zhí)行完以后,線程b才結(jié)束阻塞狀態(tài)(開始執(zhí)行)。
8.sleep(long millitime):讓當(dāng)前線程"睡眠"指定的millitime毫秒。在指定的millitime毫秒時間內(nèi),當(dāng)前線程是阻塞狀態(tài)。會拋出InterruptedException異常
* 9.isAlive():判斷當(dāng)前線程是否存活
class HelloThread extends Thread{ @Override public void run() { for(int i = 0;i < 100;i++){ if(i % 2 != 0){ try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":" +Thread.currentThread().getPriority() + ":" + i); } } } public HelloThread(String name){ super(name); } } public class ThreadMethodTest { public static void main(String[] args) { HelloThread h1 = new HelloThread("Thread:1");//通過構(gòu)造器給線程命名,但前期是得在子類中提供一個構(gòu)造器 // h1.setName("線程一"); //設(shè)置分線程的優(yōu)先級 h1.setPriority(Thread.MAX_PRIORITY); h1.start(); //給主線程命名 Thread.currentThread().setName("主線程"); Thread.currentThread().setPriority(Thread.MIN_PRIORITY); for(int i = 0;i < 100;i++){ if(i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i); } // if(i == 20){ // try { // h1.join();//join()的測試 // } catch (InterruptedException e) { // e.printStackTrace(); // } // } } } }
線程的優(yōu)先級:
1.MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5--->默認(rèn)優(yōu)先級
2.如何獲取和設(shè)置當(dāng)前線程的優(yōu)先級:
getPriority():獲取線程的優(yōu)先級
setPriority(int p):設(shè)置線程的優(yōu)先級
說明:高優(yōu)先級的線程要搶占低優(yōu)先級線程CPU的執(zhí)行權(quán),但是只是從概率上講,高優(yōu)先級的線程高概率的情況下,不一定被執(zhí)行,并不意味著只有當(dāng)高優(yōu)先級的線程執(zhí)行完畢后,低優(yōu)先級的線程才執(zhí)行。
到此這篇關(guān)于Java線程創(chuàng)建的四種方式總結(jié)的文章就介紹到這了,更多相關(guān)Java 線程創(chuàng)建內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 實戰(zhàn)項目錘煉之網(wǎng)上花店商城的實現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+jsp+servlet+mysql+ajax實現(xiàn)一個網(wǎng)上花店商城系統(tǒng),大家可以在過程中查缺補漏,提升水平2021-11-11關(guān)于Spring?@Transactional事務(wù)傳播機制詳解
我們?nèi)粘9ぷ髦袠O少使用事務(wù)傳播級別,單純只是使用事務(wù)和rollbackfor拋出異常來解決事務(wù)問題,但其實我們很多時候使用的是不正確的,或者說會造成事務(wù)粒度過大,本文詳解一下事務(wù)傳播級別,也讓自己更好地處理事務(wù)問題,需要的朋友可以參考下2023-08-08Java性能工具JMeter實現(xiàn)上傳與下載腳本編寫
性能測試工作中,文件上傳也是經(jīng)常見的性能壓測場景之一,那么 JMeter 文件上傳下載腳本怎么做,本文詳細(xì)的來介紹一下,感興趣的可以了解一下2021-07-07Spring Security @PreAuthorize注解分析
本教程介紹了如何使用 Spring 方法級安全和 @PreAuthorize 注解來保護 RestController 方法,通過這些步驟,您可以確保只有具有適當(dāng)角色或權(quán)限的用戶才能訪問特定的 REST API,感興趣的朋友跟隨小編一起看看吧2024-11-11