久久精品五月,日韩不卡视频在线观看,国产精品videossex久久发布 ,久久av综合

站長(zhǎng)資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

簡(jiǎn)單總結(jié)Java多線程知識(shí)點(diǎn)

本篇文章給大家?guī)?lái)了關(guān)于java的相關(guān)知識(shí),其中主要介紹了關(guān)于多線程的相關(guān)問(wèn)題,一個(gè)進(jìn)程可以并發(fā)多個(gè)線程,每條線程并行執(zhí)行不同的任務(wù),線程是進(jìn)程的基本單位,是一個(gè)單一順序的控制流,下面一起來(lái)看一下,希望對(duì)大家有幫助。

簡(jiǎn)單總結(jié)Java多線程知識(shí)點(diǎn)

程序員必備接口測(cè)試調(diào)試工具:立即使用
Apipost = Postman + Swagger + Mock + Jmeter
Api設(shè)計(jì)、調(diào)試、文檔、自動(dòng)化測(cè)試工具
后端、前端、測(cè)試,同時(shí)在線協(xié)作,內(nèi)容實(shí)時(shí)同步

推薦學(xué)習(xí):《java視頻教程》

Java 提供了多線程編程的內(nèi)置支持,讓我們可以輕松開(kāi)發(fā)多線程應(yīng)用。

Java 中我們最為熟悉的線程就是 main 線程——主線程。

一個(gè)進(jìn)程可以并發(fā)多個(gè)線程,每條線程并行執(zhí)行不同的任務(wù)。線程是進(jìn)程的基本單位,是一個(gè)單一順序的控制流,一個(gè)進(jìn)程一直運(yùn)行,直到所有的“非守護(hù)線程”都結(jié)束運(yùn)行后才能結(jié)束。Java 中常見(jiàn)的守護(hù)線程有:垃圾回收線程、

這里簡(jiǎn)要述說(shuō)以下并發(fā)和并行的區(qū)別。

并發(fā):同一時(shí)間段內(nèi)有多個(gè)任務(wù)在運(yùn)行

并行:同一時(shí)間點(diǎn)上有多個(gè)任務(wù)同時(shí)在運(yùn)行

多線程可以幫助我們高效地執(zhí)行任務(wù),合理利用 CPU 資源,充分地發(fā)揮多核 CPU 的性能。但是多線程也并不總是能夠讓程序高效運(yùn)行的,多線程切換帶來(lái)的開(kāi)銷、線程死鎖、線程異常等等問(wèn)題,都會(huì)使得多線程開(kāi)發(fā)較單線程開(kāi)發(fā)更麻煩。因此,有必要學(xué)習(xí) Java 多線程的相關(guān)知識(shí),從而提高開(kāi)發(fā)效率。

1 創(chuàng)建多線程

根據(jù)官方文檔 Thread (Java Platform SE 8 ) (oracle.com) 中 java.lang.Thread 的說(shuō)明,可以看到線程的創(chuàng)建方式主要有兩種:

There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started.

The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread, and started.

可以看到,有兩種創(chuàng)建線程的方式:

  • 聲明一個(gè)類繼承 Thread 類,這個(gè)子類需要重寫 run 方法,隨后創(chuàng)建這個(gè)子類的實(shí)例,這個(gè)實(shí)例就可以創(chuàng)建并啟動(dòng)一個(gè)線程執(zhí)行任務(wù);

  • 聲明一個(gè)類實(shí)現(xiàn)接口 Runnable 并實(shí)現(xiàn) run 方法。這個(gè)類的實(shí)例作為參數(shù)分配給一個(gè) Thread 實(shí)例,隨后使用 Thread 實(shí)例創(chuàng)建并啟動(dòng)線程即可

除此之外的創(chuàng)建線程的方法,諸如使用 Callable 和 FutureTask、線程池等等,無(wú)非是在此基礎(chǔ)上的擴(kuò)展,查看源碼可以看到 FutureTask 也實(shí)現(xiàn)了 Runnable 接口。

使用繼承 Thread 類的方法創(chuàng)建線程的代碼:

/**  * 使用繼承 Thread 類的方法創(chuàng)建線程  */ public class CreateOne {     public static void main(String[] args) {         Thread t = new MySubThread();         t.start();     } } class MySubThread extends Thread {     @Override     public void run() {         // currentThread() 是 Thread 的靜態(tài)方法,可以獲取正在執(zhí)行當(dāng)前代碼的線程實(shí)例         System.out.println(Thread.currentThread().getName() + "執(zhí)行任務(wù)");     } } // ================================== 運(yùn)行結(jié)果 Thread-0執(zhí)行任務(wù)
登錄后復(fù)制

使用實(shí)現(xiàn) Runnable 接口的方法創(chuàng)建線程的代碼:

/**  * 使用實(shí)現(xiàn) Runnable 接口的方法創(chuàng)建線程  */ public class CreateTwo {     public static void main(String[] args) {         RunnableImpl r = new RunnableImpl();         Thread t = new Thread(r);         t.start();     } } class RunnableImpl implements Runnable {     @Override     public void run() {         System.out.println(Thread.currentThread().getName() + "執(zhí)行任務(wù)");     } } // ================================== 運(yùn)行結(jié)果 Thread-0執(zhí)行任務(wù)
登錄后復(fù)制

1.1 孰優(yōu)孰劣

創(chuàng)建線程雖然有兩種方法,但是在實(shí)際開(kāi)發(fā)中,使用實(shí)現(xiàn)接口 Runnable 的方法更好,原因如下:

查看 Thread 的 run 方法,可以看到:

// Thread 實(shí)例的成員變量 target 是一個(gè) Runnable 實(shí)例,可以通過(guò) Thread 的構(gòu)造方法傳入 private Runnable target; // 如果有傳入 Runnable 實(shí)例,那么就執(zhí)行它的 run 方法 // 如果重寫,就完全執(zhí)行我們自己的邏輯 public void run() {     if (target != null) {         target.run();     } }
登錄后復(fù)制

查看上面的源碼,我們可以知道,Thread 類并不是定義執(zhí)行任務(wù)的主體,而是 Runnable 定義執(zhí)行任務(wù)內(nèi)容,Thread 調(diào)用執(zhí)行,從而實(shí)現(xiàn)線程與任務(wù)的解耦。

由于線程與任務(wù)解耦,我們可以復(fù)用線程,而不是當(dāng)需要執(zhí)行任務(wù)就去創(chuàng)建線程、執(zhí)行完畢就銷毀線程,這樣帶來(lái)的系統(tǒng)開(kāi)銷太大。這也是線程池的基本思想。

此外,Java 只只支持單繼承,如果繼承 Thread 使用多線程,那么后續(xù)需要通過(guò)繼承的方式擴(kuò)展功能,那會(huì)相當(dāng)麻煩。

2 start 和 run 方法

從上面可以得知,有兩種創(chuàng)建線程的方式,我們通過(guò) Thread 類或 Runnable 接口的 run 方法定義任務(wù),通過(guò) Thread 的 start 方法創(chuàng)建并啟動(dòng)線程。

我們不能通過(guò) run 方法啟動(dòng)并創(chuàng)建一個(gè)線程,它只是一個(gè)普通方法,如果直接調(diào)用這個(gè)方法,其實(shí)只是調(diào)用這個(gè)方法的線程在執(zhí)行任務(wù)罷了。

// 將上面的代碼修改一下,查看執(zhí)行結(jié)果 public class CreateOne {     public static void main(String[] args) {         Thread t = new MySubThread();         t.run();         //t.start();     } } // ===================== 執(zhí)行結(jié)果 main執(zhí)行任務(wù)
登錄后復(fù)制

查看 start 方法的源碼:

// 線程狀態(tài),為 0 表示還未啟動(dòng) private volatile int threadStatus = 0; // 同步方法,確保創(chuàng)建、啟動(dòng)線程是線程安全的 public synchronized void start() {     // 如果線程狀態(tài)不為 0,那么拋出異常——即線程已經(jīng)創(chuàng)建了     if (threadStatus != 0)         throw new IllegalThreadStateException(); // 將當(dāng)前線程添加到線程組     group.add(this);     boolean started = false;     try {         // 這是一個(gè)本地方法         start0();         started = true;     } finally {         try {             if (!started) {                 group.threadStartFailed(this);             }         } catch (Throwable ignore) {         }     } } // 由本地方法實(shí)現(xiàn),只需要知道,該方法調(diào)用后會(huì)創(chuàng)建一個(gè)線程,并且會(huì)執(zhí)行 run 方法 private native void start0();
登錄后復(fù)制

由上面的源碼可以得知:

  • 創(chuàng)建并啟動(dòng)一個(gè)線程是線程安全的

  • start() 方法不能反復(fù)調(diào)用,否則會(huì)拋出異常

3 怎么停止線程

線程并不是無(wú)休止地執(zhí)行下去的,通常情況下,線程停止的條件有:

  • run 方法執(zhí)行結(jié)束

  • 線程發(fā)生異常,但是沒(méi)有捕獲處理

除此之外,我們還需要自定義某些情況下需要通知線程停止,例如:

用戶主動(dòng)取消任務(wù)

任務(wù)執(zhí)行時(shí)間超時(shí)、出錯(cuò)

出現(xiàn)故障,服務(wù)需要快速停止

為什么不能直接簡(jiǎn)單粗暴的停止線程呢?通過(guò)通知線程停止任務(wù),我們可以更優(yōu)雅地停止線程,讓線程保存問(wèn)題現(xiàn)場(chǎng)、記錄日志、發(fā)送警報(bào)、友好提示等等,令線程在合適的代碼位置停止線程,從而避免一些數(shù)據(jù)丟失等情況。

令線程停止的方法是讓線程捕獲中斷異?;驒z測(cè)中斷標(biāo)志位,從而優(yōu)雅地停止線程,這是推薦的做法。而不推薦的做法有,使用被標(biāo)記為過(guò)時(shí)的方法:stop,resume,suspend,這些方法可能會(huì)造成死鎖、線程不安全等情況,由于已經(jīng)過(guò)時(shí)了,所以不做過(guò)多介紹。

3.1 通知線程中斷

我們要使用通知的方式停止目標(biāo)線程,通過(guò)以下方法,希望能夠幫助你掌握中斷線程的方法:

/**  * 中斷線程  */ public class InterruptThread {     public static void main(String[] args) throws InterruptedException {         Thread t = new Thread(() -> {             long i = 0;             // isInterrupted() 檢測(cè)當(dāng)前線程是否處于中斷狀態(tài)             while (i < Long.MAX_VALUE && !Thread.currentThread().isInterrupted()) {                 i++;             }             System.out.println(i);         });                  t.start();         // 主線程睡眠 1 秒,通知線程中斷         Thread.sleep(1000);         t.interrupt();     } } // 運(yùn)行結(jié)果 1436125519
登錄后復(fù)制

這是中斷線程的方法之一,還有其他方法,當(dāng)線程處于阻塞狀態(tài)時(shí),線程并不能運(yùn)行到檢測(cè)線程狀態(tài)的代碼位置,然后正確響應(yīng)中斷,這個(gè)時(shí)候,我們需要通過(guò)捕獲異常的方式停止線程:

/**  * 通過(guò)捕獲中斷異常停止線程  */ public class InterruptThreadByException {     public static void main(String[] args) throws InterruptedException {         Thread t = new Thread(()->{             long i = 0;             while (i < Long.MAX_VALUE) {                 i++;                 try {                     // 線程大部分時(shí)間處于阻塞狀態(tài),sleep 方法會(huì)拋出中斷異常 InterruptedException                     Thread.sleep(100);                 } catch (InterruptedException e) {                     // 捕獲到中斷異常,代表線程被通知中斷,做出相應(yīng)處理再停止線程                     System.out.println("線程收到中斷通知   " + i);                     // 如果 try-catch 在 while 代碼塊之外,可以不用 return 也可以結(jié)束代碼                     // 在 while 代碼塊之內(nèi),如果沒(méi)有 return / break,那么還是會(huì)進(jìn)入下一次循環(huán),并不能正確停止                     return;                 }             }         });         t.start();         Thread.sleep(1000);         t.interrupt();     } } // 運(yùn)行結(jié)果 線程收到中斷通知   10
登錄后復(fù)制

以上,就是停止線程的正確做法,此外,捕獲中斷異常后,會(huì)清除線程的中斷狀態(tài),在實(shí)際開(kāi)發(fā)中需要特別注意。例如,修改上面的代碼:

public class InterruptThreadByException {     public static void main(String[] args) throws InterruptedException {         Thread t = new Thread(()->{             long i = 0;             while (i < Long.MAX_VALUE) {                 i++;                 try {                     Thread.sleep(100);                 } catch (InterruptedException e) {                     System.out.println("線程收到中斷通知   " + i);                     //  添加這行代碼,捕獲到中斷異常后,檢測(cè)中斷狀態(tài),中斷狀態(tài)為 false                     System.out.println(Thread.currentThread().isInterrupted());                     return;                 }             }         });         t.start();         Thread.sleep(1000);         t.interrupt();     } }
登錄后復(fù)制

所以,在線程中,如果調(diào)用了其他方法,如果該方法有異常發(fā)生,那么:

將異常拋出,而不是在子方法內(nèi)部捕獲處理,由 run 方法統(tǒng)一處理異常

捕獲異常,并重新通知當(dāng)前線程中斷,Thread.currentThread().interrupt()

例如:

public class SubMethodException {     public static void main(String[] args) throws InterruptedException {         Thread t1 = new Thread(new ExceptionRunnableA());         Thread t2 = new Thread(new ExceptionRunnableB());         t1.start();         t2.start();         Thread.sleep(1000);         t1.interrupt();         t2.interrupt();     } } class ExceptionRunnableA implements Runnable {     @Override     public void run() {         try {             while (true) {                 method();             }         } catch (InterruptedException e) {             System.out.println("run 方法內(nèi)部捕獲中斷異常");         }     }     public void method() throws InterruptedException {         Thread.sleep(100000L);     } } class ExceptionRunnableB implements Runnable {     @Override     public void run() {         while (!Thread.currentThread().isInterrupted()) {             method();         }     }     public void method()  {         try {             Thread.sleep(100000L);         } catch (InterruptedException e) {             System.out.println("子方法內(nèi)部捕獲中斷異常");             // 如果不重新設(shè)置中斷,線程將不能正確響應(yīng)中斷             Thread.currentThread().interrupt();         }     } }
登錄后復(fù)制

綜上,總結(jié)出令線程正確停止的方法為:

使用 interrupt() 方法通知目標(biāo)線程停止,標(biāo)記目標(biāo)線程的中斷狀態(tài)為 true

目標(biāo)線程通過(guò) isInterrupted() 不時(shí)地檢測(cè)線程的中斷狀態(tài),根據(jù)情況決定是否停止線程

如果線程使用了阻塞方法例如 sleep(),那么需要捕獲中斷異常并處理中斷通知,捕獲了中斷異常會(huì)重置中斷標(biāo)記位

如果 run() 方法調(diào)用了其他子方法,那么子方法:

將異常拋出,傳遞到頂層 run 方法,由 run 方法統(tǒng)一處理

將異常捕獲,同時(shí)重新通知當(dāng)前線程中斷

下面再說(shuō)說(shuō)關(guān)于中斷的幾個(gè)相關(guān)方法和一些會(huì)拋出中斷異常的方法,使用的時(shí)候需要特別注意。

3.2 線程中斷的相關(guān)方法

  • interrupt() 實(shí)例方法,通知目標(biāo)線程中斷。

  • static interrupted() 靜態(tài)方法,獲取當(dāng)前線程是否處于中斷狀態(tài),會(huì)重置中斷狀態(tài),即如果中斷狀態(tài)為 true,那么調(diào)用后中斷狀態(tài)為 false。方法內(nèi)部通過(guò) Thread.currentThread() 獲取執(zhí)行線程實(shí)例。

  • isInterrupted() 實(shí)例方法,獲取線程的中斷狀態(tài),不會(huì)清除中斷狀態(tài)。

3.3 阻塞并能響應(yīng)中斷的方法

  • Object.wait()

  • Thread.sleep()

  • Thread.join()

  • BlockingQueue.take() / put()

  • Lock.lockInterruptibly()

  • CountDownLatch.await()

  • CyclicBarrier.await()

  • Exchanger.exchange()

4 線程的生命周期

線程的生命周期狀態(tài)由六部分組成:

簡(jiǎn)單總結(jié)Java多線程知識(shí)點(diǎn)

可以用一張圖總結(jié)線程的生命周期,以及各個(gè)過(guò)程之間是如何轉(zhuǎn)換的:

簡(jiǎn)單總結(jié)Java多線程知識(shí)點(diǎn)

5 Thread 和 Object 中的線程方法

現(xiàn)在,我們已經(jīng)知道了線程的創(chuàng)建、啟動(dòng)、停止以及線程的生命周期了,那么,再來(lái)看看線程相關(guān)的方法有哪些。

首先,看看 Thread 中的一些方法:

簡(jiǎn)單總結(jié)Java多線程知識(shí)點(diǎn)

再看看 Object 中的相關(guān)方法:

簡(jiǎn)單總結(jié)Java多線程知識(shí)點(diǎn)

運(yùn)行以下代碼,查看 wait() 和 sleep() 是否會(huì)釋放同步鎖

/** * 證明 sleep 不會(huì)釋放鎖,wait 會(huì)釋放鎖 */ public class SleepAndWait {     private static Object lock = new Object();     public static void main(String[] args) {         Thread t1 = new Thread(()->{             synchronized (lock) {                 System.out.println(Thread.currentThread().getName() + "獲得同步鎖,調(diào)用 wait() 方法");                 try {                     lock.wait(2000);                     System.out.println(Thread.currentThread().getName() + "重新獲得同步鎖");                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }         });         Thread t2 = new Thread(()->{             synchronized (lock) {                 System.out.println(Thread.currentThread().getName() + "獲得同步鎖,喚醒另一個(gè)線程,調(diào)用 sleep()");                 lock.notify();                 try {                     // 如果 sleep() 會(huì)釋放鎖,那么在此期間,上面的線程將會(huì)繼續(xù)運(yùn)行,即 sleep 不會(huì)釋放同步鎖                     Thread.sleep(2000);                     // 如果執(zhí)行 wait 方法,那么上面的線程將會(huì)繼續(xù)執(zhí)行,證明 wait 方法會(huì)釋放鎖                     //lock.wait(2000);                     System.out.println(Thread.currentThread().getName() + "sleep 結(jié)束");                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }         });         t1.start();         t2.start();     } }
登錄后復(fù)制

上面的代碼已經(jīng)證明了 sleep() 不會(huì)釋放同步鎖,此外,sleep() 也不會(huì)釋放 Lock 的鎖,運(yùn)行以下代碼查看結(jié)果:

/**  * sleep 不會(huì)釋放 Lock 鎖  */ public class SleepDontReleaseLock implements Runnable {     private static Lock lock = new ReentrantLock();     @Override     public void run() {         // 調(diào)用 lock 方法,線程會(huì)嘗試持有該鎖對(duì)象,如果已經(jīng)被其他線程鎖住,那么當(dāng)前線程會(huì)進(jìn)入阻塞狀態(tài)         lock.lock();         try {             System.out.println(Thread.currentThread().getName() + "獲得 lock 鎖");             // 如果 sleep 會(huì)釋放 Lock 鎖,那么另一個(gè)線程會(huì)馬上打印上面的語(yǔ)句             Thread.sleep(1000);             System.out.println(Thread.currentThread().getName() + "釋放 lock 鎖");         } catch (InterruptedException e) {             e.printStackTrace();         } finally {             // 當(dāng)前線程釋放鎖,讓其他線程可以占有鎖             lock.unlock();         }     }     public static void main(String[] args) {         SleepDontReleaseLock task = new SleepDontReleaseLock();         new Thread(task).start();         new Thread(task).start();     } }
登錄后復(fù)制

5.1 wait 和 sleep 的異同

接下來(lái)總結(jié) Object.wait() 和 Thread.sleep() 方法的異同點(diǎn)。

相同點(diǎn):

  • 都會(huì)使線程進(jìn)入阻塞狀態(tài)

  • 都可以響應(yīng)中斷

不同點(diǎn):

  • wait() 是 Object 的實(shí)例方法,sleep() 是 Thread 的靜態(tài)方法

  • sleep() 需要指定時(shí)間

  • wait() 會(huì)釋放鎖,sleep() 不會(huì)釋放鎖,包括同步鎖和 Lock 鎖

  • wait() 必須配合 synchronized 使用

6 線程的相關(guān)屬性

現(xiàn)在我們已經(jīng)對(duì) Java 中的多線程有一定的了解了,我們?cè)倏纯?Java 中線程 Thread 的一些相關(guān)屬性,即它的成員變量。

簡(jiǎn)單總結(jié)Java多線程知識(shí)點(diǎn)

運(yùn)行以下代碼,了解線程的相關(guān)屬性

public class ThreadFields {     public static void main(String[] args) throws InterruptedException {         Thread t = new Thread(() -> {             // 自定義線程的 ID 并不是從 2 開(kāi)始             System.out.println("線程 " + Thread.currentThread().getName()                                + " 的線程 ID " + Thread.currentThread().getId());             while (true) {                 // 守護(hù)線程一直運(yùn)行,但是 用戶線程即這里的主線程結(jié)束后,也會(huì)隨著虛擬機(jī)一起停止             }         });         // 自定義線程名字         t.setName("自定義線程");         // 將其設(shè)置為守護(hù)線程         t.setDaemon(true);         // 設(shè)置優(yōu)先級(jí) Thread.MIN_PRIORITY = 1     Thread.MAX_PRIORITY = 10         t.setPriority(Thread.MIN_PRIORITY);         t.start();         // 主線程的 ID 為 1         System.out.println("線程 " + Thread.currentThread().getName() + " 的線程 ID " + Thread.currentThread().getId());         Thread.sleep(3000);     } }
登錄后復(fù)制

7 全局異常處理

在子線程中,如果發(fā)生了異常我們能夠及時(shí)捕獲并處理,那么對(duì)程序運(yùn)行并不會(huì)有什么惡劣影響。

但是,如果發(fā)生了一些未捕獲的異常,在多線程情況下,這些異常打印出來(lái)的堆棧信息,很容易淹沒(méi)在龐大的日志中,我們可能很難察覺(jué)到,并且不好排查問(wèn)題。

如果對(duì)這些異常都做捕獲處理,那么就會(huì)造成代碼的冗余,編寫起來(lái)也不方便。

因此,我們可以編寫一個(gè)全局異常處理器來(lái)處理子線程中拋出的異常,統(tǒng)一地處理,解耦代碼。

7.1 源碼查看

在講解如何處理子線程的異常問(wèn)題前,我們先看看 JVM 默認(rèn)情況下,是如何處理未捕獲的異常的。

查看 Thread 的源碼:

public class Thread implements Runnable {     【1】當(dāng)發(fā)生未捕獲的異常時(shí),JVM 會(huì)調(diào)用該方法,并傳遞異常信息給異常處理器         可以在這里打下斷點(diǎn),在線程中拋出異常不捕獲,IDEA 會(huì)跳轉(zhuǎn)到這里     // 向處理程序發(fā)送未捕獲的異常。此方法僅由JVM調(diào)用。 private void dispatchUncaughtException(Throwable e) {         【2】查看第 9 行代碼,可以看到如果沒(méi)有指定異常處理器,默認(rèn)是線程組作為異常處理器         【3】調(diào)用這個(gè)異常處理器的處理方法,處理異常,查看第 15 行         getUncaughtExceptionHandler().uncaughtException(this, e);     }          public UncaughtExceptionHandler getUncaughtExceptionHandler() {         return uncaughtExceptionHandler != null ?             uncaughtExceptionHandler : group;     }          【4】UncaughtExceptionHandler 是 Thread 的內(nèi)部接口,線程組也是該接口的實(shí)現(xiàn),         只有一個(gè)方法處理異常,接下來(lái)查看第 25 行,看看 Group 是如何實(shí)現(xiàn)的     @FunctionalInterface     public interface UncaughtExceptionHandler {         void uncaughtException(Thread t, Throwable e);     } } public class ThreadGroup implements Thread.UncaughtExceptionHandler {     【5】默認(rèn)異常處理器的實(shí)現(xiàn)     public void uncaughtException(Thread t, Throwable e) {         // 如果有父線程組,交給它處理         if (parent != null) {             parent.uncaughtException(t, e);         } else {             // 獲取默認(rèn)的異常處理器,如果沒(méi)有指定,那么為 null             Thread.UncaughtExceptionHandler ueh =                 Thread.getDefaultUncaughtExceptionHandler();             if (ueh != null) {                 ueh.uncaughtException(t, e);             }              // 沒(méi)有指定異常處理器,打印堆棧信息             else if (!(e instanceof ThreadDeath)) {                 System.err.print("Exception in thread ""                                  + t.getName() + "" ");                 e.printStackTrace(System.err);             }         }     } }
登錄后復(fù)制

7.2 自定義全局異常處理器

通過(guò)上面的源碼講解,已經(jīng)可以知道 JVM 是如何處理未捕獲的異常的了,即只打印堆棧信息。那么,要如何自定義異常處理器呢?

具體方法為:

實(shí)現(xiàn)接口 Thread.UncaughtExceptionHandler 并實(shí)現(xiàn)方法 uncaughtException()

為創(chuàng)建的線程指定異常處理器

示例代碼:

public class MyExceptionHandler implements Thread.UncaughtExceptionHandler{     @Override     public void uncaughtException(Thread t, Throwable e) {         System.out.println("發(fā)生了未捕獲的異常,進(jìn)行日志處理、報(bào)警處理、友好提示、數(shù)據(jù)備份等等......");         e.printStackTrace();     }     public static void main(String[] args) {         Thread t = new Thread(() -> {             throw new RuntimeException();         });         t.setUncaughtExceptionHandler(new MyExceptionHandler());         t.start();     } }
登錄后復(fù)制

8 多線程帶來(lái)的問(wèn)題

合理地利用多線程能夠帶來(lái)性能上的提升,但是如果因?yàn)橐恍┦杪?,多線程反而會(huì)成為程序員的噩夢(mèng)。

例如,多線程開(kāi)發(fā),我們需要考慮線程安全問(wèn)題、性能問(wèn)題。

首先,講講線程安全問(wèn)題。

什么是線程安全?所謂線程安全,即

在多線程情況下,如果訪問(wèn)某個(gè)對(duì)象,不需要額外處理,例如加鎖、令線程阻塞、額外的線程調(diào)度等,調(diào)用這個(gè)對(duì)象都能獲得正確的結(jié)果,那么這個(gè)對(duì)象就是線程安全的

因此,在編寫多線程程序時(shí),就需要考慮某個(gè)數(shù)據(jù)是否是線程安全的,如果這個(gè)對(duì)象滿足:

  • 被多個(gè)線程共享

  • 操作具有時(shí)序要求,先讀后寫

  • 這個(gè)對(duì)象的類有他人編寫,并且沒(méi)有聲明是線程安全的

那么我們就需要考慮使用同步鎖、Lock、并發(fā)工具類(java.util.concurrent)來(lái)保證這個(gè)對(duì)象是在多線程下是安全的。

再看看多線程帶來(lái)的性能問(wèn)題。

多個(gè)線程的調(diào)度需要上下文切換,這需要耗費(fèi) CPU 資源。

所謂上下文,即處理器中寄存器、程序計(jì)數(shù)器內(nèi)的信息。

上下文切換,即 CPU 掛起一個(gè)線程,將其上下文保存到內(nèi)存中,從內(nèi)存中獲取另一個(gè)運(yùn)行線程的上下文,恢復(fù)到寄存器中,根據(jù)程序計(jì)數(shù)器中的指令恢復(fù)線程運(yùn)行。

一個(gè)線程被掛起,另一個(gè)線程恢復(fù)運(yùn)行,這個(gè)時(shí)候,被掛起的線程的數(shù)據(jù)緩存對(duì)于運(yùn)行線程來(lái)說(shuō)是無(wú)效的,減緩了線程的運(yùn)行速度,新的線程需要重新緩存數(shù)據(jù)提升運(yùn)行速度。

通常情況下,密集的 IO 操作、搶鎖操作都會(huì)帶來(lái)密集的上下文切換。

以上,是上下文切換帶來(lái)的性能問(wèn)題,Java 的內(nèi)存模型也會(huì)帶來(lái)性能問(wèn)題,為了保證數(shù)據(jù)的可見(jiàn)性,JVM 會(huì)強(qiáng)制令數(shù)據(jù)緩存失效,保證數(shù)據(jù)是實(shí)時(shí)最新的,這也犧牲了緩存帶來(lái)的性能提升。

9 總結(jié)

這里總結(jié)下上面的內(nèi)容。

  • 創(chuàng)建線程有兩種方式,繼承 Thread 和實(shí)現(xiàn) Runnable

  • start 方法才能正確創(chuàng)建和啟動(dòng)線程,run 方法只是一個(gè)普通方法

  • start 方法不能反復(fù)調(diào)用,反復(fù)調(diào)用會(huì)拋出異常

  • 正確停止線程的方法是通過(guò) interrupt() 通知線程

線程不時(shí)地檢查中斷狀態(tài)并判斷是否停止線程,使用方法 isInterrupt()

如果線程阻塞,捕獲中斷異常,判斷是否停止線程

線程調(diào)用的子方法最好將異常拋出,由 run 方法統(tǒng)一捕獲處理

線程調(diào)用的子方法如果捕獲異常,需要重新通知線程中斷

  • 線程的生命周期為

NEW

RUNNABLE

BLOCKED

WAITING

TIMED WAITING

TERMINATED

  • wait()/notify()/notifyAll() 必須配合同步鎖使用

  • wait() 會(huì)釋放鎖,sleep() 不會(huì)釋放鎖,包括同步鎖和 Lock 鎖

  • 線程的一些屬性

線程ID,無(wú)法修改

線程名 name,可以自定義

守護(hù)線程 daemon,線程類型會(huì)繼承自父線程,通常不指定線程為守護(hù)線程

優(yōu)先級(jí) priority,通常使用默認(rèn)優(yōu)先級(jí),不改變優(yōu)先級(jí)

  • 可以自定義全局異常處理器,處理非主線程中的未捕獲的異常,如備份數(shù)據(jù)、日志處理、報(bào)警等等

  • 多線程開(kāi)發(fā)會(huì)帶來(lái)線程安全問(wèn)題、性能問(wèn)題,開(kāi)發(fā)過(guò)程需要特別注意

推薦學(xué)習(xí):《java視頻教程》

贊(0)
分享到: 更多 (0)
?
網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)
久久精品五月,日韩不卡视频在线观看,国产精品videossex久久发布 ,久久av综合
精品视频黄色| 国产精品字幕| 男女性色大片免费观看一区二区| 久久99蜜桃| 国产一精品一av一免费爽爽| 日韩欧乱色一区二区三区在线| 蘑菇福利视频一区播放| 自拍日韩欧美| 伊人久久婷婷| 免费视频一区三区| 午夜国产一区二区| 美女网站一区| 影音国产精品| 久久高清免费观看| 亚洲最大av| 亚州av日韩av| 国产日产一区| 欧美国产视频| 国产精品嫩草影院在线看| 91福利精品在线观看| 日本麻豆一区二区三区视频| 日韩中文字幕视频网| 日韩高清不卡一区| 国产精品探花在线观看| 老鸭窝一区二区久久精品| 精品三区视频| 亚洲涩涩在线| 婷婷亚洲综合| 亚洲精品第一| 国产精品亚洲四区在线观看| 久久午夜影院| 日韩av福利| 99在线精品免费视频九九视 | 亚洲在线免费| 日韩激情中文字幕| 国产亚洲精品美女久久久久久久久久| 国产精一区二区| 人在线成免费视频| 九九久久婷婷| 日韩高清在线不卡| 久久久久黄色| 99久久亚洲精品蜜臀| 久久亚洲欧美| 国产精品巨作av| 久久激情网站| 爽好多水快深点欧美视频| 亚洲天堂日韩在线| 国产精品入口久久| 999久久久精品国产| 久久www成人_看片免费不卡| 日韩av影院| 97在线精品| 午夜在线一区二区| 美腿丝袜亚洲三区| 亚洲国产一区二区三区在线播放| 日本成人中文字幕在线视频| 美女高潮久久久| 日韩一区二区久久| 国产精品久久乐| 91久久视频| 狂野欧美性猛交xxxx| 久久电影tv| 午夜精品影视国产一区在线麻豆| 国产不卡精品在线| 综合干狼人综合首页| 欧美男人天堂| 久久国产三级| 伊人精品视频| 色婷婷色综合| 亚洲免费毛片| 日韩免费视频| 亚洲精品四区| 婷婷综合六月| 亚洲少妇诱惑| 久久影院一区二区三区| 久久黄色影院| 亚洲精选av| 国产三级一区| 韩国女主播一区二区三区| 热久久国产精品| 国产成人精品一区二区免费看京| 尤物在线精品| 国产精品激情| 欧美成人午夜| 国产精品亚洲四区在线观看| 99国产精品免费视频观看| 中文字幕一区二区av| 久久精品国产网站| 国产精品日韩欧美一区| 福利一区和二区| 蜜臀av亚洲一区中文字幕| 国产日韩免费| 久久久国产精品一区二区中文| 亚州国产精品| 日韩精品免费一区二区三区| 日韩一区二区三区免费视频| 成人精品久久| 亚洲日产国产精品| 日韩毛片视频| 免费人成网站在线观看欧美高清| 亚洲成av人片一区二区密柚 | 丝袜美腿一区| 国产日产一区| aⅴ色国产欧美| 精品欧美日韩精品| 在线一区视频| 色在线中文字幕| 国产欧美一区二区色老头| 国产专区一区| 欧美激情 亚洲a∨综合| 99国产精品99久久久久久粉嫩| 中文在线а√天堂| 欧美日韩视频免费看| 久久亚洲国产| 久久伊人久久| 蜜臀精品久久久久久蜜臀| 亚洲日本网址| 国产精品22p| 亚洲资源在线| 欧美日韩视频| 高清日韩欧美| 91成人小视频| 青草国产精品| 蜜桃久久久久久| 91精品观看| 亚洲欧洲高清| 九九久久国产| 国产精品一区三区在线观看| 亚洲综合激情在线| 成人av二区| 国产+成+人+亚洲欧洲在线| 欧美日韩一区二区高清| 亚洲香蕉视频| 国产色综合网| 欧美不卡高清| 在线一区av| 日韩av专区| 国产一区二区三区不卡视频网站 | 午夜久久av| 亚洲欧美日韩国产一区二区| 久久中文视频| 国产成人精品三级高清久久91| 成人污污视频| 国语精品一区| 免费精品一区| 国产精品久久| 国产剧情在线观看一区| 亚洲综合图色| 亚洲毛片视频| 亚洲人成亚洲精品| 丝瓜av网站精品一区二区| 欧美天堂亚洲电影院在线观看| 精品日韩视频| 欧美日韩尤物久久| 日韩网站中文字幕| 亚洲成人精品| 激情欧美一区二区三区| 夜夜嗨av一区二区三区网站四季av| 五月天综合网站| 欧美日韩四区| 亚洲深夜福利| 国产一区91| 99精品99| 午夜亚洲一区| 蜜桃视频在线观看一区| 中文字幕av一区二区三区四区| 视频一区中文字幕| 亚洲色图综合| 久久久国产精品入口麻豆| 久久亚洲黄色| 91视频一区| 久久久久国产一区二区| 丝袜美腿一区| 五月天久久网站| 久久午夜精品| 91精品视频一区二区| 亚洲黄色免费看| 日本久久成人网| 婷婷中文字幕一区| 丝袜美腿高跟呻吟高潮一区| 亚洲精品一级| 国产精品tv| 在线观看精品| 亚洲一区二区三区四区五区午夜 | 久久婷婷久久| 亚洲欧美日韩综合国产aⅴ| 久久亚洲二区| 捆绑调教美女网站视频一区| 日韩毛片视频| 亚洲国产一区二区三区在线播放 | 午夜精品久久久久久久久久蜜桃| 色老板在线视频一区二区| 激情久久久久久久| 麻豆成人在线| 国产精品一区高清| 日韩视频网站在线观看| 欧美午夜不卡| 免费在线观看视频一区| 国产乱人伦丫前精品视频| 成人亚洲一区二区|