<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 進程和線程概念 ### 進程概念 進程是一個很抽象的概念,指在系統中能獨立運行并作為資源分配的基本單位,它是由一組機器指令、數據和堆棧等組成的,是一個能獨立運行的活動實體。具有一定獨立功能的程序關于某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。進程簡單來理解就是每個應用都是一個進程。 ### 線程概念 線程:是用來執行具體功能和任務的,需要進程為載體,是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。是并發執行的程序在執行過程中分配和管理資源的基本單位,是一個動態概念,竟爭計算機系統資源的基本單位。 注:一個程序至少有一個進程,一個進程至少有一個線程(主線程),進程在執行過程中擁有獨立的內存單元,而多個線程共享內存,從而極大地提高了程序的運行效率。 ## 進程和線程的區別 1. 操作系統資源管理方式是不一樣的,進程有獨立的地址空間,進程崩潰后會有保護模式讓其不會對其他的進程產生影響。而線程則不然,線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,所以一個線程掛掉可能影響整個進程掛掉。 2. 進程的并發性沒有線程高。 3. 每個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口。但是線程不能夠獨立執行,必須依存在應用程序中。由應用程序提供多個線程執行控制。 4. 對于應用程序來說,多線程是可以同時有多個執行部分同時執行。但對于操作系統來說是沒有將多個線程看做多個獨立的應用,來實現進程的調度和管理以及資源分配 注:多線程容易調度,有效地實現并發性。對內存的開銷比較小。創建線程比創建進程要快。 ## 線程的狀態轉換 ### 新建狀態(New) 創建一個線程對象后,該線程對象就處于新建狀態,此時它不能運行,和其他Java對象一樣,僅僅由Java虛擬機為其分配了內存,沒有表現出任何線程的動態特征。 ### 就緒狀態(Runnable) 當線程對象調用了start()方法后,該線程就進入就緒狀態(也稱可運行狀態)。處于就緒狀態的線程位于可運行池中,此時它只是具備了運行的條件,能否獲得 CPU 的使用權開始運行,還需要等待系統的調度。 ### 運行狀態(Running) 如果處于就緒狀態的線程獲得了 CPU 的使用權,開始執行run()方法中的線程執行體,則該線程處于運行狀態。當一個線程啟動后,它不可能一直處于運行狀態(除非它的線程執行體足夠短,瞬間就結束了),當使用完系統分配的時間后,系統就會剝奪該線程占用的 CPU 資源,讓其他線程獲得執行的機會。需要注意的是,只有處于就緒狀態的線程才可能轉換到運行狀態。 ### 阻塞狀態(Blocked) 一個正在執行的線程在某些特殊情況下,如執行耗時的輸入/輸出操作時,會放棄CPU 的使用權,進入阻塞狀態。線程進入阻塞狀態后,就不能進入排隊隊列。只有當引起阻塞的原因被消除后,線程才可以轉入就緒狀態。下面就列舉一下線程由運行狀態轉換成阻塞狀態的原因,以及如何從阻塞狀態轉換成就緒狀態。 - 當線程試圖獲取某個對象的同步鎖時,如果該鎖被其他線程所持有,則當前線程會進入阻塞狀態,如果想從阻塞狀態進入就緒狀態必須得獲取到其他線程所持有的鎖。 - 當線程調用了一個阻塞式的IO 方法時,該線程就會進入阻塞狀態,如果想進入就緒狀態就必須要等到這個阻塞的IO 方法返回。 - 當線程調用了某個對象的 wait()方法時,也會使線程進入阻塞狀態,如果想進入就緒狀態就需要使用notify()方法喚醒該線程。 - 當線程調用了 Thread的sleep(longmillis)方法時,也會使線程進入阻塞狀態,在這種情況下,只需等到線程睡眠的時間到了以后,線程就會自動進入就緒狀態。 - 當在一個線程中調用了另一個線程的join()方法時,會使當前線程進入阻塞狀態,在這種情況下,需要等到新加入的線程運行結束后才會結束阻塞狀態,進入就緒狀態。 需要注意的是,線程從阻塞狀態只能進入就緒狀態,而不能直接進入運行狀態,也就是說結束阻塞的線程需要重新進入可運行池中,等待系統的調度。 ### 死亡狀態(Terminated) 線程的run()方法正常執行完畢或者線程拋出一個未捕獲的異常(Exception)、錯誤(Error),線程就進入死亡狀態。一旦進入死亡狀態,線程將不再擁有運行的資格,也不能再轉換到其他狀態。 ## 線程狀態轉換圖 ![](http://47.107.171.232/easily-j/images/20190309/e1ba3031-659f-4707-9cc1-7b219640f4c2.jpg) ## 線程的調度 線程的優先級 線程休眠 線程讓步 線程插隊 ## 多線程同步 線程安全 同步代碼塊 同步方法 死鎖問題 ## 多線程的6種實現方式 多線程的形式上實現方式主要有兩種,一種是繼承Thread類,一種是實現Runnable接口。本質上實現方式都是來實現線程任務,然后啟動線程執行線程任務(這里的線程任務實際上就是run方法)。這里所說的6種,實際上都是在以上兩種的基礎上的一些變形。 ### 一、繼承Thread類 萬物皆對象,那么線程也是對象,對象就應該能夠抽取其公共特性封裝成為類,使用類可以實例化多個對象,那么實現線程的第一種方式就是繼承Thread類的方式。繼承Thread類是最簡單的一種實現線程的方式,通過jdk給我們提供的Thread類,重寫Thread類的run方法即可,那么當線程啟動的時候,就會執行run方法體的內容。代碼如下: ```java package com.hy.thread.t1; /** * 繼承Thread類的方式實現多線程演示 */ public class ThreadDemo extends Thread { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印當前線程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { ThreadDemo td = new ThreadDemo(); td.start(); // 啟動線程 while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印當前線程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` 運行結果如下 ```java main is running ... Thread-0 is running ... main is running ... Thread-0 is running ... Thread-0 is running ... main is running ... Thread-0 is running ... main is running ... ``` 這里要注意,在啟動線程的時候,我們并不是調用線程類的run方法,而是調用了線程類的start方法。那么我們能不能調用run方法呢?答案是肯定的,因為run方法是一個public聲明的方法,因此我們是可以調用的,但是如果我們調用了run方法,那么這個方法將會作為一個普通的方法被調用,并不會開啟線程。這里實際上是采用了設計模式中的模板方法模式,Thread類作為模板,而run方法是在變化的因此放到子類。 **創建多個線程** 上面的例子中除了我們創建的一個線程以外其實還有一個主線程也在執行。除了這兩個線程以外還有沒有其他的線程在執行了呢,其實是有的,比如我們看不到的垃圾回收線程,也在默默的執行。這里我們并不去考慮有多少個線程在執行,上面我們自己創建了一個線程,那么能不能多創建幾個一起執行呢,答案是肯定的。一個Thread類就是一個線程對象,那么多創建幾個Thread類,并調用其start方法就可以啟動多個線程了。代碼如下 ```java package com.hy.thread.t1; public class MultiThreadDemo extends Thread { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印當前線程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { // 創建四個線程對象,代表四個線程 MultiThreadDemo td1 = new MultiThreadDemo(); MultiThreadDemo td2 = new MultiThreadDemo(); MultiThreadDemo td3 = new MultiThreadDemo(); MultiThreadDemo td4 = new MultiThreadDemo(); td1.start(); // 啟動線程 td2.start(); // 啟動線程 td3.start(); // 啟動線程 td4.start(); // 啟動線程 while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印當前線程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` 運行結果如下 ```java main is running ... Thread-2 is running ... Thread-1 is running ... Thread-3 is running ... Thread-0 is running ... Thread-3 is running ... Thread-2 is running ... main is running ... Thread-1 is running ... Thread-0 is running ... Thread-1 is running ... main is running ... Thread-2 is running ... Thread-0 is running ... Thread-3 is running ... ``` 我們發現這里有個問題,多個線程的名字都是系統定義好的,就是Thread-開頭,后面跟數字,如果我們每個線程處理不同的任務,那么我們能不能給線程起上不同的名字,方便我們排查問題呢?答案是可以的。只要在創建線程實例的時候,在構造方法中傳入指定的線程名稱即可。如下 ```java package com.hy.thread.t1; public class MultiThreadDemo extends Thread { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印當前線程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 指定線程名稱的構造方法 * * @param name */ public MultiThreadDemo(String name) { super(name); } public static void main(String[] args) { // 創建四個線程對象,代表四個線程 MultiThreadDemo td1 = new MultiThreadDemo("t1"); // 指定線程的名字 MultiThreadDemo td2 = new MultiThreadDemo("t2"); MultiThreadDemo td3 = new MultiThreadDemo("t3"); MultiThreadDemo td4 = new MultiThreadDemo("t4"); td1.start(); // 啟動線程 td2.start(); // 啟動線程 td3.start(); // 啟動線程 td4.start(); // 啟動線程 while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印當前線程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` 運行的結果如下: ```java main is running ... t1 is running ... t2 is running ... t3 is running ... t4 is running ... main is running ... t1 is running ... t2 is running ... t4 is running ... t3 is running ... ``` ### 二、實現Runnable接口 實現Runnable接口也是一種常見的創建線程的方式。使用接口的方式可以讓我們的程序降低耦合度。Runnable接口中僅僅定義了一個方法,就是run。我們來看一下Runnable接口的代碼。 ```java package java.lang; @FunctionalInterface public interface Runnable { public abstract void run(); } ``` 其實Runnable就是一個線程任務,線程任務和線程的控制分離,這也就是上面所說的解耦。**我們要實現一個線程,可以借助Thread類,Thread類要執行的任務就可以由實現了Runnable接口的類來處理。** 這就是Runnable的精髓之所在! 使用Runnable實現上面的例子步驟如下: 1. 定義一個類實現Runnable接口,作為線程任務類 2. 重寫run方法,并實現方法體,方法體的代碼就是線程所執行的代碼 3. 定義一個可以運行的類,并在main方法中創建線程任務類 4. 創建Thread類,并將線程任務類做為Thread類的構造方法傳入 5. 啟動線程 線程任務類代碼如下 ```java package com.hy.thread.t2; public class ThreadTarget implements Runnable { @Override public void run() { while(true) { System.out.println(Thread.currentThread().getName() + " is running .. "); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` 可運行類代碼如下 ```java package com.hy.thread.t2; public class Main { public static void main(String[] args) { ThreadTarget tt = new ThreadTarget(); // 實例化線程任務類 Thread t = new Thread(tt); // 創建線程對象,并將線程任務類作為構造方法參數傳入 t.start(); // 啟動線程 // 主線程的任務,為了演示多個線程一起執行 while(true) { System.out.println(Thread.currentThread().getName() + " is running .. "); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` 線程任務和線程的控制分離,那么一個線程任務可以提交給多個線程來執行。這是很有用的,比如車站的售票窗口,每個窗口可以看做是一個線程,他們每個窗口做的事情都是一樣的,也就是售票。這樣我們程序在模擬現實的時候就可以定義一個售票任務,讓多個窗口同時執行這一個任務。那么如果要改動任務執行計劃,只要修改線程任務類,所有的線程就都會按照修改后的來執行。相比較繼承Thread類的方式來創建線程的方式,實現Runnable接口是更為常用的。 ### 三、使用內部類的方式 這并不是一種新的實現線程的方式,只是另外的一種寫法。比如有些情況我們的線程就想執行一次,以后就用不到了。那么像上面兩種方式都還要再定義一個類,顯得比較麻煩,我們就可以通過匿名內部類的方式來實現。使用內部類實現依然有兩種,分別是繼承Thread類和實現Runnable接口。代碼如下: ```java package com.hy.thread.t3; public class DemoThread { public static void main(String[] args) { // 基于子類的實現 new Thread() { public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印當前線程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } }; }.start(); // 基于接口的實現 new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印當前線程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); // 主線程的方法 while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印當前線程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` 可以想象一下,我能不能既基于接口,又基于子類呢?像下面的代碼會執行出什么樣子呢? ```java package com.hy.thread.t3; public class DemoThred2 { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println("runnable is running ... "); // 打印當前線程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } }) { public void run() { while (true) { System.out.println("sub is running ... "); // 打印當前線程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } }; }.start(); } } ``` 運行結果如下: ```java sub is running ... sub is running ... sub is running ... ``` 我們可以看到,其實是基于子類的執行了,為什么呢,其實很簡單,我們先來看一下為什么不基于子類的時候Runnable的run方法可以執行。這個要從Thread的源碼看起,下面是我截取的代碼片段。 ```java public Thread(Runnable target) init(null, target, "Thread-" + nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null, true); } private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name; Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { /* Determine if it's an applet or not */ /* If there is a security manager, ask the security manager what to do. */ if (security != null) { g = security.getThreadGroup(); } /* If the security doesn't have a strong opinion of the matter use the parent thread group. */ if (g == null) { g = parent.getThreadGroup(); } } /* checkAccess regardless of whether or not threadgroup is explicitly passed in. */ g.checkAccess(); /* * Do we have the required permissions? */ if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); } } g.addUnstarted(); this.group = g; this.daemon = parent.isDaemon(); this.priority = parent.getPriority(); if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); this.target = target; // 注意這里 setPriority(priority); if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; /* Set thread ID */ tid = nextThreadID(); } ``` 其實上面的眾多代碼就是為了表現 this.target = target 那么target是什么呢,是Thread類的成員變量。那么在什么地方用到了target呢?下面是run方法的內容。 ```java @Override public void run() { if (target != null) { target.run(); } } ``` 我們可以看到,如果通過上面的構造方法傳入target,那么就會執行target中的run方法。可能有朋友就會問了,我們同時繼承Thread類和實現Runnable接口,target不為空,那么為何不執行target的run呢。不要忘記了,我們在子類中已經重寫了Thread類的run方法,因此run方法已經不在是我們看到的這樣了。那當然也就不回執行target的run方法。 ### 四、定時器 定時器可以說是一種基于線程的一個工具類。可以定時的來執行某個任務。比如要在凌晨的時候匯總一些數據,比如要每隔10分鐘抓取一次某個網站上的數據等等,總之計時器無處不在。我們一般將需要定時完成的任務稱之為計劃任務,這在很多的系統中是非常常見的,比如linux的計劃任務,比如Windows下的任務計劃等等。我們自己的系統中也需要很多定時執行的也都需要計劃任務。最簡單的計劃任務就可以通過jdk給我提供的API來實現,當然也有很多的計劃任務的框架,比如spring的schedule以及著名的quartz。我們這里不去討論其他的計劃任務框架,我們就來看一下jdk所給我們提供的API來實現定時任務。 - 例1:在2017年10月11日晚上10點執行任務。 ```java package com.roocon.thread.t3; import java.text.SimpleDateFormat; import java.util.Timer; import java.util.TimerTask; /** * 定時器舉例 * */ public class TimerDemo { private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); public static void main(String[] args) throws Exception { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { System.out.println("定時任務執行了...."); } }, format.parse("2017-10-11 22:00:00")); } } ``` - 例2: 每隔5s執行一次 ```java package com.roocon.thread.t3; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TimerDemo2 { public static void main(String[] args) { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { System.out.println("Hello"); } }, new Date(), 5000); } } ``` > 關于Spring的定時任務,可以通過spring的教程來學習。 ### 五:帶返回值的線程 我們發現上面提到的不管是繼承Thread類還是實現Runnable接口,發現有兩個問題,第一個是無法拋出更多的異常,第二個是線程執行完畢之后并無法獲得線程的返回值。那么下面的這種實現方式就可以完成我們的需求。這種方式的實現就是我們后面要詳細介紹的Future模式,只是在jdk5的時候,官方給我們提供了可用的API,我們可以直接使用。但是使用這種方式創建線程比上面兩種方式要復雜一些,步驟如下。 1. 創建一個類實現Callable接口,實現call方法。這個接口類似于Runnable接口,但比Runnable接口更加強大,增加了異常和返回值。 2. 創建一個FutureTask,指定Callable對象,做為線程任務。 3. 創建線程,指定線程任務。 4. 啟動線程 代碼如下: ```java package com.roocon.thread.t4; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; public class CallableTest { public static void main(String[] args) throws Exception { Callable<Integer> call = new Callable<Integer>() { @Override public Integer call() throws Exception { System.out.println("thread start .. "); Thread.sleep(2000); return 1; } }; FutureTask<Integer> task = new FutureTask<>(call); Thread t = new Thread(task); t.start(); System.out.println("do other thing .. "); System.out.println("拿到線程的執行結果 : " + task.get()); } } ``` 執行結果如下: ```java do other thing .. thread start .. 拿到線程的執行結果 : 1 ``` > Callable中可以通過范型參數來指定線程的返回值類型。通過FutureTask的get方法拿到線程的返回值。 ### 六:基于線程池的方式 我們知道,線程和數據庫連接這些資源都是非常寶貴的資源。那么每次需要的時候創建,不需要的時候銷毀,是非常浪費資源的。那么我們就可以使用緩存的策略,也就是使用線程池。當然了,線程池也不需要我們來實現,jdk的官方也給我們提供了API。 代碼如下: ```java package com.roocon.thread.t5; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolDemo { public static void main(String[] args) { // 創建線程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); while(true) { threadPool.execute(new Runnable() { // 提交多個線程任務,并執行 @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running .."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } } ``` 執行結果如下: ```java pool-1-thread-4 is running .. pool-1-thread-1 is running .. pool-1-thread-6 is running .. pool-1-thread-2 is running .. pool-1-thread-8 is running .. pool-1-thread-3 is running .. pool-1-thread-5 is running .. pool-1-thread-9 is running .. pool-1-thread-10 is running .. pool-1-thread-7 is running .. ``` 線程池的內容還有非常多,這里不再詳細地講解。 ### Spring方式:使用Spring來實現多線程 在我們的應用系統中,經常會處理一些耗時任務,自然而然的會想到使用多線程。JDK給我們提供了非常方便的操作線程的API,JDK5之后更是新增了JUC包的支持,并發編程大師Doug Lea(JDK并發的作者)也是一直在為我們使用線程做著不懈的努力。 為什么還要使用Spring來實現多線程呢?這是句廢話!實際有兩個原因,第一使用Spring比使用JDK原生的并發API更簡單。第二我們的應用環境一般都會集成Spring,我們的Bean也都交給Spring來進行管理,那么使用Spring來實現多線程更加簡單,更加優雅。(更多的可能是在環境中就要這么用!!) 在Spring3之后,Spring引入了對多線程的支持,如果你使用的版本在3.1以前,應該還是需要通過傳統的方式來實現多線程的。從Spring3同時也是新增了Java的配置方式,而且Java配置方式也逐漸成為主流的Spring的配置方式,因此后面的例子都是以Java的配置進行演示。 廢話有點多,下面具體說說該如何在Spring中實現多線程,其實非常簡單,只需要在配置類中添加@EnableAsync就可以使用多線程。在希望執行的并發方法中使用@Async就可以定義一個線程任務。通過spring給我們提供的ThreadPoolTaskExecutor就可以使用線程池。下面舉個例子來說明 #### 首先定義配置類 ```java package com.hy.spring.test7; import java.util.concurrent.Executor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration @ComponentScan("com.hy.spring.test7") @EnableAsync // 啟用異步任務 public class ThreadConfig { // 這里是聲明一個bean,類似于xml中的<bean>標簽。 // Executor 就是一個線程池 @Bean public Executor getExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); executor.initialize(); return executor; } } ``` #### 定義要執行的任務 ```java package com.hy.spring.test7; import java.util.Random; import java.util.UUID; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service // 注解的方式把AsyncService交給Spring來管理 public class AsynTaskService { // 這里可以注入spring中管理的其他bean,這也是使用spring來實現多線程的一大優勢 @Async // 這里進行標注為異步任務,在執行此方法的時候,會單獨開啟線程來執行 public void f1() { System.out.println("f1 : " + Thread.currentThread().getName() + " " + UUID.randomUUID().toString()); try { Thread.sleep(new Random().nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } } @Async public void f2() { System.out.println("f2 : " + Thread.currentThread().getName() + " " + UUID.randomUUID().toString()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } ``` #### 測試類 ```java package com.hy.spring.test7; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ThreadConfig.class); AsynTaskService service = context.getBean(AsynTaskService.class); for (int i = 0; i < 10; i++) { service.f1(); // 執行異步任務 service.f2(); } context.close(); } } ``` #### 輸出結果 ```java f1 : ThreadPoolTaskExecutor-5 20e6ba88-ae51-42b9-aac6-ed399419fe6d f2 : ThreadPoolTaskExecutor-2 0d7b1da4-e045-4d58-9054-e793f931cae1 f2 : ThreadPoolTaskExecutor-4 17b8d7c7-24e3-4bcf-b4da-822650a8f0be f1 : ThreadPoolTaskExecutor-3 a9b32322-1c9b-4fc7-9c2a-1f7a81f2b089 f1 : ThreadPoolTaskExecutor-1 13a85fde-73c7-4c9b-9bb2-92405d1d3ac4 f2 : ThreadPoolTaskExecutor-3 8896caaf-381c-4fc3-ab0f-a42fcc25e5fd f1 : ThreadPoolTaskExecutor-5 48246589-f8e9-4e9c-b017-8586bf14c0b0 f2 : ThreadPoolTaskExecutor-1 291b03ea-154f-46ba-bc41-69a61d1dd4d5 ``` 可以看到我們兩個任務是異步進行的。 下面關于線程池的配置還有一種方式,就是直接實現AsyncConfigurer接口,重寫getAsyncExecutor方法即可,代碼如下 ```java package com.hy.spring.test7; import java.util.concurrent.Executor; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration @ComponentScan("com.hy.spring.test7") @EnableAsync public class ThreadConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return null; } } ``` > 原文:[java多線程的6種實現方式詳解](https://blog.csdn.net/king_kgh/article/details/78213576 "java多線程的6種實現方式詳解"),感謝博主的支持,謝謝QAQ
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看