日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java 并发——多线程基础

發(fā)布時(shí)間:2025/3/13 java 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java 并发——多线程基础 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Thead類與Runnable接口

Java的線程,即一個(gè)Thread實(shí)例。

Java的線程執(zhí)行過程有兩種實(shí)現(xiàn)方式:

  • 子類繼承Thread類,并且重寫void run()方法。
  • 自定義類實(shí)現(xiàn)Runnable接口,并且實(shí)現(xiàn)void run()方法。并在Thead構(gòu)造時(shí),將Runnable實(shí)例放入Thead。

?

Thread類

創(chuàng)建一個(gè)新線程必須實(shí)例化一個(gè)Thread對象。

使用方法:

  • 子類繼承Thread類。重寫Thread的run()方法。
  • 實(shí)例化該子類。
  • 執(zhí)行Thread的start()方法啟動一個(gè)線程。
  • 例:Thread使用方法

    /** * Thread類。 */ class MyThread extends Thread { public void run() {//... 線程執(zhí)行 } } public class TestThread1 {public static void main(String args[]) {MyThread thread = new MyThread (); //新建線程。thread .start(); //線程開始執(zhí)行。//主線程其他方法 } }

    ?

    ?

    Runnable接口

    實(shí)現(xiàn)Runnable接口需要實(shí)現(xiàn)run()方法.run()方法代表線程需要完成的任務(wù).因此把run方法稱為線程執(zhí)行體.

    使用方法:

  • 類實(shí)現(xiàn)Runnable接口,并且實(shí)現(xiàn)run()方法。
  • 實(shí)例化該類。Runnable runnable=new MyRun();
  • 把該類注入到Thread對象中,即通過Thread的構(gòu)造方法注入。Thread thread=new Thread(runnable);
  • 調(diào)用Thread實(shí)例的start()方法。啟動線程。thread.start();
  • 例:Runnable使用方法

    class MyRunner implements Runnable { //實(shí)現(xiàn)Runnable接口public void run() {// ...... 線程執(zhí)行 } }public class TestThread1 {public static void main(String args[]) {MyRunner runner= new Runner1(); Thread t = new Thread(runner); //新建線程。t.start(); //線程開始執(zhí)行。// ...... 主線程繼續(xù)執(zhí)行 }}

    ?

    ?

    兩種方式所創(chuàng)建線程的對比

    ??? 實(shí)現(xiàn)Runnable接口方式的多線程:

    • 編程稍復(fù)雜。
    • 如果需要訪問當(dāng)前線程,必須使用Thread.currentThread()方法。
    • 線程只是實(shí)現(xiàn)了Runnable接口,還可以繼承其他類。
    • 在這種方式下,可以多個(gè)線程共享同一個(gè)target對象,所以非常適合多個(gè)相同線程來處理同一份資源的情況,從而可以將CPU,代碼,數(shù)據(jù)分開,形成清晰的模型,較好地體現(xiàn)了面向?qū)ο蟮乃枷搿?/li>

    ??? 繼承Thread類方式的多線程:

    • 編程簡單。
    • 如果需要訪問當(dāng)前線程直接使用this。
    • 已經(jīng)繼承了Thread類,無法再繼承其他父類。

    ?

    ?

    Thread的常用API

    1? Thread類的構(gòu)造方法

    • Thread()
    • Thread(Runnable target)
    • Thread(Runnable target, String name)
    • Thread(String name)
    • Thread(ThreadGroup group, Runnable target)
    • Thread(ThreadGroup group, Runnable target, String name)
    • Thread(ThreadGroup group, Runnable target, String name, long stackSize)
    • Thread(ThreadGroup group, String name)

    ?

    2常用方法

    (1) 啟動線程

    • void run() :如果該線程是使用獨(dú)立的 Runnable 運(yùn)行對象構(gòu)造的,則調(diào)用該 Runnable 對象的 run 方法;否則,該方法不執(zhí)行任何操作并返回。
    • void start() :使該線程開始執(zhí)行;Java 虛擬機(jī)調(diào)用該線程的 run 方法。

    ?

    (2) 線程狀態(tài)控制

    • boolean isAlive() ? ?測試線程是否處于活動狀態(tài)。
    • int getPriority() ? ?返回線程的優(yōu)先級。
    • void setPriority(int newPriority)?? ?更改線程的優(yōu)先級。
    • static void sleep(long millis)
    • static void sleep(long millis, int nanos) ? ?當(dāng)前線程暫停運(yùn)行指定的毫秒數(shù)(加指定的納秒數(shù)),但此線程不失去已獲得的鎖旗標(biāo).
    • void join() ?? ?等待該線程終止。
    • void join(long millis) ? ?等待該線程終止的時(shí)間最長為 millis 毫秒。
    • void join(long millis, int nanos) ? ?等待該線程終止的時(shí)間最長為 millis 毫秒 + nanos 納秒。
    • void interrupt() ? ?中斷線程。 調(diào)用該方法引發(fā)線程拋出InterruptedException異常。
    • static boolean interrupted() ? ?測試當(dāng)前線程是否已經(jīng)中斷。
    • boolean isInterrupted() ? ?測試線程是否已經(jīng)中斷。
    • static void yield() ? ?暫停當(dāng)前正在執(zhí)行的線程對象,并執(zhí)行其他線程。
    • Thread.State getState() ? ?返回該線程的狀態(tài)。

    ?

    (3) 當(dāng)前線程

    • static Thread currentThread() : 返回對當(dāng)前正在執(zhí)行的線程對象的引用。
    • String getName() : 返回該線程的名稱.
    • void setName(String name) : 設(shè)置線程名稱.
    • void setDaemon(boolean on) 將該線程標(biāo)記為守護(hù)線程或用戶線程。
    • boolean isDaemon() :測試該線程是否為守護(hù)線程。
    • String toString() : 返回該線程的字符串表示形式,包括線程名稱 優(yōu)先級和線程組。
    • long getId() :返回該線程的標(biāo)識符。
    • void checkAccess() :判定當(dāng)前運(yùn)行的線程是否有權(quán)修改該線程。
    • ClassLoader getContextClassLoader() :返回該線程的上下文 ClassLoader。
    • static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() :返回線程由于未捕獲到異常而突然終止時(shí)調(diào)用的默認(rèn)處理程序。
    • Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() :返回該線程由于未捕獲到異常而突然終止時(shí)調(diào)用 的處理程序。

    ?

    (4) 線程組

    • static int enumerate(Thread[] tarray) :將當(dāng)前線程的線程組及其子組中的每一個(gè)活動線程復(fù)制到指定的數(shù)組中。
    • StackTraceElement[] getStackTrace() :返回一個(gè)表示該線程堆棧轉(zhuǎn)儲的堆棧跟蹤元素?cái)?shù)組。
    • static int activeCount() :返回當(dāng)前線程的線程組中活動線程的數(shù)目。
    • ThreadGroup getThreadGroup() :返回該線程所屬的線程組。

    ?

    (5) 其他

    • static Map<Thread,StackTraceElement[]> getAllStackTraces() 返回所有活動線程的堆棧跟蹤的一個(gè)映射。

    ?

    ?

    線程的生命周期

    ??

    ?

    1? 新建和就緒狀態(tài)

    ??? 當(dāng)程序使用new關(guān)鍵字創(chuàng)建了一個(gè)線程后,該線程就處于新建狀態(tài)。JVM為Thread對象分配內(nèi)存。初始化其成員變量的值。線程對象調(diào)用start()方法之后,該線程處于就緒狀態(tài)。 JVM會為其創(chuàng)建方法調(diào)用棧和程序計(jì)數(shù)器。
    ? ? 就緒狀態(tài)的線程并沒有開始運(yùn)行,它只是表示該線程可以運(yùn)行了。JVM的線程調(diào)度器調(diào)度該線程運(yùn)行。

    ? ? 注意:

    • 調(diào)用start()啟動線程之后,run()方法才會被看作線程執(zhí)行體。
    • 直接調(diào)用run()方法,則run()方法就只是一個(gè)普通方法。

    ?

    2? 運(yùn)行和阻塞狀態(tài)

    ? ? 就緒狀態(tài)的線程獲得了CPU,開始執(zhí)行run方法的線程執(zhí)行體。則該線程處于運(yùn)行狀態(tài)。線程在執(zhí)行過程中可能會被中斷,以使其他線程獲得執(zhí)行的機(jī)會,線程調(diào)度取決于底層平臺采用的策略。

    ? ? 現(xiàn)代桌面和服務(wù)器操作系統(tǒng)一般都采用搶占式策略。一些小型設(shè)備如手機(jī)則可能采用協(xié)作式調(diào)度。?搶占式策略的系統(tǒng):系統(tǒng)給每個(gè)可執(zhí)行的線程一個(gè)小時(shí)間段來處理任務(wù);當(dāng)該時(shí)間段用完,系統(tǒng)會剝奪該線程所占有的資源,讓其他線程獲得執(zhí)行機(jī)會.在選擇下一個(gè)線程時(shí),系統(tǒng)會考慮線程的優(yōu)先級.

    ?

    3? 線程進(jìn)入阻塞狀態(tài)

  • 線程調(diào)用sleep方法主動放棄所占用的處理器資源。
  • 線程調(diào)用了一個(gè)阻塞式的IO方法,該方法返回之前,該線程被阻塞。
  • 線程試圖獲得一個(gè)同步監(jiān)視器,但同步監(jiān)視器正被其他線程所持有。
  • 線程在等待某個(gè)通知(notify)。
  • 線程調(diào)用了線程的suspend方法將線程掛起。不過這個(gè)方法容易導(dǎo)致死鎖,所以程序應(yīng)該盡量避免使用該方法。
  • ?

    4? 阻塞線程重寫進(jìn)入就緒狀態(tài)

  • 調(diào)用sleep方法的線程經(jīng)過了指定的時(shí)間。
  • 線程調(diào)用的阻塞式IO方法已經(jīng)返回。
  • 線程成功地獲得了試圖取得同步監(jiān)視器。
  • 線程正在等待某個(gè)通知時(shí),其他線程發(fā)出了一個(gè)通知。
  • 處于關(guān)閉狀態(tài)的線程被調(diào)用了resume恢復(fù)方法。
  • ?

    5? 線程死亡

    ??? 線程在以下情況下死亡:

    • run()方法執(zhí)行完成,線程正常結(jié)束.
    • 線程拋出一個(gè)未捕獲的Exception或Error.
    • 直接調(diào)用該線程的stop()方法來結(jié)束該線程.該方法容易導(dǎo)致死鎖,通常不推薦使用.

    ??? Thread對象的isAlive()方法,查看線程是否死亡。

    ? ? 注意:

    • 死亡的線程不能再用start()方法重新啟動。
    • 一個(gè)線程的start()方法不能兩次調(diào)用。

    ?

    6? 線程睡眠:sleep

    ??? sleep方法將線程轉(zhuǎn)入阻塞狀態(tài)。時(shí)間到即再轉(zhuǎn)入就緒狀態(tài)。

    ?

    ?

    ?

    線程的操作與特性

    1 ?線程讓步: yeild

    yeild()?方法是靜態(tài)方法。該方法使當(dāng)前線程讓出CPU資源,繼續(xù)參與線程競爭。

    ?

    2 ?join線程

    public final void join() throws InterruptedException public final synchronized void join(long millis) throws InterruptedException

    ?

    join()方法表示當(dāng)前線程等待指定線程結(jié)束,等待的線程結(jié)束后繼續(xù)執(zhí)行當(dāng)前線程

    ?

    例:join方法示例。

    public class JoinMain {public volatile static int i=0; public static class AddThread extends Thread{ @Override public void run() {for(i=0;i<10000000;i++); }}public static void main(String[] args) throws InterruptedException { AddThread at=new AddThread(); at.start(); at.join(); // 當(dāng)前線程(主線程)等待at線程運(yùn)行結(jié)束。 System.out.println(i); } }

    ?

    ?

    ?

    join的本質(zhì)

    查看join方法的源碼,可以了解到,join即相當(dāng)于以下代碼:

    ?

    while ( 指定的線程.isAlive()) {wait(0); }

    ?

    ?

    ?

    當(dāng)前線程會一直調(diào)用wait()方法,所以當(dāng)前線程會一直處于等待狀態(tài)。當(dāng)被等待的線程結(jié)束(即指定的線程結(jié)束),JVM會自動調(diào)用notifyAll()方法來通知當(dāng)前線程wait已經(jīng)結(jié)束。注意,此處不要手動調(diào)用thread.notifyAll()方法。(關(guān)于join方法,JDK文檔有更詳細(xì)的說明)

    ?

    ?

    3 ?線程優(yōu)先級

    ??? 每個(gè)線程都有優(yōu)先級。優(yōu)先級高的線程獲得較多的執(zhí)行機(jī)會。默認(rèn)情況下,main線程具有普通優(yōu)先級。每個(gè)線程默認(rèn)優(yōu)先級與創(chuàng)建它的父線程具有同樣的優(yōu)先級。

    ??? Java提供的優(yōu)先級范圍是1~10。默認(rèn)優(yōu)先級為5。

    ??? Thread提供靜態(tài)常量:

    • static int MAX_PRIORITY 線程可以具有的最高優(yōu)先級。 值為10。
    • static int MIN_PRIORITY 線程可以具有的最低優(yōu)先級。值為1。
    • static int NORM_PRIORITY 分配給線程的默認(rèn)優(yōu)先級。值為5。

    ?

    ??? 注意:

    • 不同操作系統(tǒng)的優(yōu)先級不同.應(yīng)盡量避免直接為線程指定優(yōu)先級,而應(yīng)使用以上三個(gè)靜態(tài)常量類設(shè)置優(yōu)先級.

    ?

    ?

    4? 后臺線程

    ??? 運(yùn)行在后臺,用于為用戶線程提供服務(wù)。又稱為“守護(hù)線程”。所有用戶線程都結(jié)束后,后臺線程也會結(jié)束,JVM退出。

    • main方法的主線程是前臺線程。
    • 前臺線程創(chuàng)建的線程默認(rèn)是前臺線程。后臺線程創(chuàng)建的線程默認(rèn)是后臺線程。?

    ?

    ? ? 比較:

    • 普通線程:若主線程結(jié)束,普通線程不會結(jié)束,JVM不會退出。
    • 守護(hù)線程:若主線程以及所有普通線程都結(jié)束,則后臺線程會直接死亡,JVM退出。

    ? ? 常用API ? ?

    • ? ? 調(diào)用Thread對象的setDeamon(true)方法可以將指定線程設(shè)置成后臺線程。
    • ??? Thread對象的isDeamon()方法用于判斷指定線程是否為后臺線程。

    ?

    ?

    5? 停止線程

    若需要停止線程,不推薦使用stop()方法。stop方法強(qiáng)制線程停止,切線程會釋放所有monitor。由于stop方法過于粗暴,已經(jīng)被廢棄。

    ?

    例:現(xiàn)在有兩條記錄。兩條線程的其中一個(gè)線程要寫對象,另一個(gè)線程要讀對象。兩個(gè)線程對對象加鎖。

    若使用stop方法,可能導(dǎo)致數(shù)據(jù)一致性的錯(cuò)誤。

    public class StopThreadTest {static Student stu = new Student();public static void main(String[] args) throws InterruptedException {Thread writeThread = new Thread(){@Overridepublic void run() {// 假設(shè)writeThread比readThread先拿到stu的鎖。synchronized(stu){stu.id = 2; // 可能該語句執(zhí)行完后,主線程中 writeThread.stop(); 開始產(chǎn)生作用。// 若主線程執(zhí)行 writeThread.stop();則該線程在此處將強(qiáng)行結(jié)束。 java_label_stop:stu.name = "小王";}}};Thread readThread = new Thread(){@Overridepublic void run() {// 假設(shè)writeThread比readThread先拿到stu的鎖。synchronized(stu){// 若在java_label_stop處writeThread被結(jié)束,則該語句將打印出 2 ; 小明 。從而導(dǎo)致數(shù)據(jù)一致性錯(cuò)誤System.out.println(stu.id + ";" + stu.name);}}};// 主線程設(shè)置stu。stu.id = 1;stu.name = "小明";// 假設(shè)writeThread比readThread先拿到stu的鎖。 writeThread.start();readThread.start();// 強(qiáng)制停止寫線程。 writeThread.stop();}}class Student{public int id;public String name; }

    ?

    ?

    ?

    ?

    6 線程中斷

    public void Thread.interrupt() // 中斷線程。修改線程的中斷狀態(tài),但線程本身不會有任何響應(yīng),依舊運(yùn)行。public boolean Thread.isInterrupted() // 判斷是否被中斷。public static boolean Thread.interrupted() // 判斷是否被中斷,并清除當(dāng)前中斷狀態(tài)

    調(diào)用線程中斷方法只是給線程“打個(gè)招呼”,線程不會本身不會做任何相應(yīng)。

    ?

    例:調(diào)用interrupt()方法,線程依舊運(yùn)行。

    public class InterruptThreadTest {public static void main(String[] args) {Thread t1 = new Thread(){@Overridepublic void run(){ while(true){ Thread.yield(); } }};t1.start(); // 啟動線程。t1.interrupt(); // 終端線程,線程會依舊運(yùn)行。 } }

    ??

    線程中斷方法可以用于結(jié)束線程,非常優(yōu)雅方便。

    例:線程外部使用中斷方法,結(jié)束線程。

    public class InterruptStopThreadTest {public static void main(String[] args) {Thread t1 = new Thread() {@Overridepublic void run() {while (true) {// 檢測線程被中斷if (Thread.currentThread().isInterrupted()) {// 若線程中斷,則推出run方法。System.out.println("Interruted!");break;}Thread.yield();}}};t1.start(); // 啟動線程。t1.interrupt(); // 終端線程,但線程依舊運(yùn)行。t1.isInterrupted()方法將返回true。 } }

    ?

    ?

    InterruptedException異常

    大部分線程的等待方法都會拋出InterruptedException異常,中斷標(biāo)志位將會被清空。Java方法中默認(rèn)的等待線程一旦被interrupt(),則等待方法會立即拋出InterruptedException異常。

    例:處于sleep的線程被interrupt,立即拋出InterruptedException。

    public class InterruptedExceptionTest {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread() {@Overridepublic void run() {while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println("Interruted!");break;}try {Thread.sleep(2000);} catch (InterruptedException e) {System.out.println("Interruted When Sleep");// 拋出異常后會清除中斷標(biāo)記位。所以重新設(shè)置中斷狀態(tài)。 Thread.currentThread().interrupt();}Thread.yield();}}};t1.start();// 讓主線程在恰當(dāng)?shù)臅r(shí)間去中斷t1。Thread.sleep(1000);// 此時(shí)t1處于sleep狀態(tài)。interrupt() 方法會使t1中的sleep方法拋出InterruptedException異常。 t1.interrupt();} }

    打印輸出:

    Interruted When Sleep Interruted!

    ?

    ?

    ?

    ?

    ?

    7 suspend與resume

    suspend() 方法表示將線程掛起。resume() 方法表示繼續(xù)執(zhí)行掛起的線程。

    但需要注意suspend()不會釋放鎖。如果加鎖發(fā)生在resume()之前,則發(fā)生死鎖。

    這兩個(gè)方法都已經(jīng)被廢棄,不要使用。

    ?

    例:運(yùn)行以下程序,程序?qū)⒈绘i死。

    package sjq.thread.suspend_resume; package sjq.thread.suspend_resume;public class SuspendResumeThreadTest {public static Object u = new Object();public static ChangeObjectThread t1 = new ChangeObjectThread("t1");public static ChangeObjectThread t2 = new ChangeObjectThread("t2");public static class ChangeObjectThread extends Thread {public ChangeObjectThread(String name){super.setName(name);}@Overridepublic void run() {synchronized(u){System.out.println("in " + super.getName());Thread.currentThread().suspend();}}}public static void main(String[] args) throws InterruptedException {t1.start();Thread.sleep(100);t2.start();t1.resume(); // 主線程通知t1要釋放resume,但t1線程可能還未執(zhí)行suspend,當(dāng)t1執(zhí)行了suspend后,則t1將永遠(yuǎn)被掛起。且不會釋放資源。t2.resume(); // 主線程通知t2要釋放resume,但t2線程可能還未執(zhí)行suspend,當(dāng)t2執(zhí)行了suspend后,則t2將永遠(yuǎn)被掛起。且不會釋放資源。 t1.join();t2.join();} }

    ?

    控制臺顯示:

    ?

    該程序通過jstack查看線程情況,發(fā)現(xiàn)t2線程處于RUNNABLE狀態(tài),被suspend0方法掛起。且t2擁有Object的鎖,只要t2線程不結(jié)束,Object的鎖就不會被釋放。

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    轉(zhuǎn)載于:https://www.cnblogs.com/shijiaqi1066/p/3369836.html

    總結(jié)

    以上是生活随笔為你收集整理的Java 并发——多线程基础的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。