精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

300行代碼帶你秒懂Java多線程!

開發 后端
線程(英語:Thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。

線程

線程的概念,百度是這樣解釋的:

線程(英語:Thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以并發多個線程,每條線程并行執行不同的任務。在Unix System V及SunOS中也被稱為輕量進程(Lightweight Processes),但輕量進程更多指內核線程(Kernel Thread),而把用戶線程(User Thread)稱為線程。

1.1 線程與進程的區別

進程:指在系統中正在運行的一個應用程序;程序一旦運行就是進程;進程——資源分配的最小單位。

線程:系統分配處理器時間資源的基本單元,或者說進程之內獨立執行的一個單元執行流。線程——程序執行的最小單位。

也就是,進程可以包含多個線程,而線程是程序執行的最小單位。

1.2 線程的狀態

  •  NEW:線程剛創建
  •  RUNNABLE: 在JVM中正在運行的線程,其中運行狀態可以有運行中RUNNING和READY兩種狀態,由系統調度進行狀態改變。
  •  BLOCKED:線程處于阻塞狀態,等待監視鎖,可以重新進行同步代碼塊中執行
  •  WAITING : 等待狀態
  •  TIMED_WAITING: 調用sleep() join() wait()方法可能導致線程處于等待狀態
  •  TERMINATED: 線程執行完畢,已經退出

1.3 Notify和Wait :

Notify和Wait 的作用

首先看源碼給出的解釋,這里翻譯了一下:

Notify:喚醒一個正在等待這個對象的線程監控。如果有任何線程正在等待這個對象,那么它們中的一個被選擇被喚醒。選擇是任意的,發生在執行的酌情權。一個線程等待一個對象通過調用一個{@code wait}方法進行監視。

Notify()需要在同步方法或同步塊中調用,即在調用前,線程也必須獲得該對象的對象級別鎖

Wait:導致當前線程等待,直到另一個線程調用{@link java.lang.Object#notify()}方法或{@link java.lang.Object#notifyAll()}方法。

換句話說,這個方法的行為就像它簡單一樣執行調用{@code wait(0)}。當前線程必須擁有該對象的監視器。

線程釋放此監視器的所有權,并等待另一個線程通知等待該對象的監視器的線程,喚醒通過調用{@code notify}方法或{@code notifyAll}方法。然后線程等待,直到它可以重新取得監視器的所有權,然后繼續執行。

Wait()的作用是使當前執行代碼的線程進行等待,它是Object類的方法,該方法用來將當前線程置入預執行隊列中,并且在Wait所在的代碼行處停止執行,直到接到通知或被中斷為止。

在調用Wait方法之前,線程必須獲得該對象的對象級別鎖,即只能在同步方法或同步塊中調用Wait方法。

Wait和Sleep的區別:

  •  它們最大本質的區別是,Sleep()不釋放同步鎖,Wait()釋放同步鎖。
  •  還有用法的上的不同是:Sleep(milliseconds)可以用時間指定來使他自動醒過來,如果時間不到你只能調用Interreput()來強行打斷;Wait()可以用Notify()直接喚起。
  •  這兩個方法來自不同的類分別是Thread和Object
  •  最主要是Sleep方法沒有釋放鎖,而Wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。

1.4 Thread.sleep() 和Thread.yield()的異同

  •  相同 :Sleep()和yield()都會釋放CPU。
  •  不同:Sleep()使當前線程進入停滯狀態,所以執行Sleep()的線程在指定的時間內肯定不會執行;yield()只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態后馬上又被執行。Sleep()可使優先級低的線程得到執行的機會,當然也可以讓同優先級和高優先級的線程有執行的機會;yield()只能使同優先級的線程有執行的機會。

1.5 補充:死鎖的概念

死鎖:指兩個或兩個以上的進程(或線程)在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處于死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。

死鎖產生的四個必要條件(缺一不可):

  •  互斥條件:顧名思義,線程對資源的訪問是排他性,當該線程釋放資源后下一線程才可進行占用。
  •  請求和保持:簡單來說就是自己拿的不放手又等待新的資源到手。線程T1至少已經保持了一個資源R1占用,但又提出對另一個資源R2請求,而此時,資源R2被其他線程T2占用,于是該線程T1也必須等待,但又對自己保持的資源R1不釋放。
  •  不可剝奪:在沒有使用完資源時,其他線性不能進行剝奪。
  •  循環等待:一直等待對方線程釋放資源。

我們可以根據死鎖的四個必要條件破壞死鎖的形成。

1.6 補充:并發和并行的區別

并發:是指在某個時間段內,多任務交替的執行任務。當有多個線程在操作時,把CPU運行時間劃分成若干個時間段,再將時間段分配給各個線程執行。在一個時間段的線程代碼運行時,其它線程處于掛起狀。

并行:是指同一時刻同時處理多任務的能力。當有多個線程在操作時,CPU同時處理這些線程請求的能力。

區別就在于CPU是否能同時處理所有任務,并發不能,并行能。

1.7 補充:線程安全三要素

  •  原子性:Atomic包、CAS算法、Synchronized、Lock。
  •  可見性:Synchronized、Volatile(不能保證原子性)。
  •  有序性:Happens-before規則。

1.8 補充:如何實現線程安全

  •  互斥同步:Synchronized、Lock。
  •  非阻塞同步:CAS。
  •  無需同步的方案:如果一個方法本來就不涉及共享數據,那它自然就無需任何同步操作去保證正確性。

1.9 補充:保證線程安全的機制:

  •  Synchronized關鍵字
  •  Lock
  •  CAS、原子變量
  •  ThreadLocl:簡單來說就是讓每個線程,對同一個變量,都有自己的獨有副本,每個線程實際訪問的對象都是自己的,自然也就不存在線程安全問題了。
  •  Volatile
  •  CopyOnWrite寫時復制

隨著CPU核心的增多以及互聯網迅速發展,單線程的程序處理速度越來越跟不上發展速度和大數據量的增長速度,多線程應運而生,充分利用CPU資源的同時,極大提高了程序處理速度。

創建線程的方法

繼承Thread類: 

  1. public class ThreadCreateTest {  
  2.     public static void main(String[] args) {  
  3.         new MyThread().start();  
  4.     }  
  5.  
  6. class MyThread extends Thread {  
  7.     @Override  
  8.     public void run() {  
  9.         System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId());  
  10.     }  

實現Runable接口: 

  1. public class RunableCreateTest {  
  2.     public static void main(String[] args) {  
  3.         MyRunnable runnable = new MyRunnable();  
  4.         new Thread(runnable).start();  
  5.     }  
  6.  
  7. class MyRunnable implements Runnable {  
  8.     @Override  
  9.     public void run() {  
  10.         System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId());  
  11.     }  

通過Callable和Future創建線程: 

  1. public class CallableCreateTest {  
  2.     public static void main(String[] args) throws Exception {  
  3.          // 將Callable包裝成FutureTask,FutureTask也是一種Runnable  
  4.         MyCallable callable = new MyCallable();  
  5.         FutureTask<Integer> futureTask = new FutureTask<>(callable);  
  6.         new Thread(futureTask).start();  
  7.         // get方法會阻塞調用的線程  
  8.         Integer sum = futureTask.get();  
  9.         System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + "=" + sum);  
  10.     }  
  11.  
  12. class MyCallable implements Callable<Integer> {  
  13.     @Override  
  14.     public Integer call() throws Exception {  
  15.         System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tstarting...");  
  16.         int sum = 0 
  17.         for (int i = 0; i <= 100000; i++) {  
  18.             sum += i;  
  19.         }  
  20.         Thread.sleep(5000);  
  21.         System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tover...");  
  22.         return sum;  
  23.     }  

線程池方式創建:

實現Runnable接口這種方式更受歡迎,因為這不需要繼承Thread類。在應用設計中已經繼承了別的對象的情況下,這需要多繼承(而Java不支持多繼承,但可以多實現啊),只能實現接口。同時,線程池也是非常高效的,很容易實現和使用。

實際開發中,阿里巴巴開發插件一直提倡使用線程池創建線程,原因在下方會解釋,所以上面的代碼我就只簡寫了一些Demo。

2.1 線程池創建線程

線程池,顧名思義,線程存放的地方。和數據庫連接池一樣,存在的目的就是為了較少系統開銷,主要由以下幾個特點:

降低資源消耗。通過重復利用已創建的線程降低線程創建和銷毀造成的消耗(主要)。

提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。

提高線程的可管理性。線程是稀缺資源,如果無限制地創建,不僅會消耗系統資源,還會降低系統的穩定性。

Java提供四種線程池創建方式:

  •  newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
  •  newFixedThreadPool創建一個定長線程池,可控制線程最大并發數,超出的線程會在隊列中等待。
  •  newScheduledThreadPool創建一個定長線程池,支持定時及周期性任務執行。
  •  newSingleThreadExecutor創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。

通過源碼我們得知ThreadPoolExecutor繼承自AbstractExecutorService,而AbstractExecutorService實現了ExecutorService。 

  1. public class ThreadPoolExecutor extends AbstractExecutorService  
  2. public abstract class AbstractExecutorService implements ExecutorService 

2.2 ThreadPoolExecutor介紹

實際項目中,用的最多的就是ThreadPoolExecutor這個類,而《阿里巴巴Java開發手冊》中強制線程池不允許使用Executors去創建,而是通過New ThreadPoolExecutor實例的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。

我們從ThreadPoolExecutor入手多線程創建方式,先看一下線程池創建的最全參數。   

  1. public ThreadPoolExecutor(int corePoolSize,  
  2.                              int maximumPoolSize,  
  3.                              long keepAliveTime,  
  4.                              TimeUnit unit,  
  5.                              BlockingQueue<Runnable> workQueue,  
  6.                              ThreadFactory threadFactory,  
  7.                              RejectedExecutionHandler handler) {  
  8.        if (corePoolSize < 0 ||  
  9.            maximumPoolSize <= 0 ||  
  10.            maximumPoolSize < corePoolSize ||  
  11.            keepAliveTime < 0 
  12.            throw new IllegalArgumentException();  
  13.        if (workQueue == null || threadFactory == null || handler == null)  
  14.            throw new NullPointerException();  
  15.        this.corePoolSize = corePoolSize;  
  16.        this.maximumPoolSize = maximumPoolSize;  
  17.        this.workQueue = workQueue;  
  18.        this.keepAliveTime = unit.toNanos(keepAliveTime);  
  19.        this.threadFactory = threadFactory;  
  20.        this.handler = handler;  
  21.    } 

參數說明如下:

  •  corePoolSize:線程池的核心線程數,即便線程池里沒有任何任務,也會有corePoolSize個線程在候著等任務。
  •  maximumPoolSize:最大線程數,不管提交多少任務,線程池里最多工作線程數就是maximumPoolSize。
  •  keepAliveTime:線程的存活時間。當線程池里的線程數大于corePoolSize時,如果等了keepAliveTime時長還沒有任務可執行,則線程退出。
  •  Unit:這個用來指定keepAliveTime的單位,比如秒:TimeUnit.SECONDS。
  •  BlockingQueue:一個阻塞隊列,提交的任務將會被放到這個隊列里。
  •  threadFactory:線程工廠,用來創建線程,主要是為了給線程起名字,默認工廠的線程名字:pool-1-thread-3。
  •  handler:拒絕策略,當線程池里線程被耗盡,且隊列也滿了的時候會調用。

2.2.1BlockingQueue

對于BlockingQueue個人感覺還需要單獨拿出來說一下。

BlockingQueue:阻塞隊列,有先進先出(注重公平性)和先進后出(注重時效性)兩種,常見的有兩種阻塞隊列:ArrayBlockingQueue和LinkedBlockingQueue

隊列的數據結構大致如圖:

隊列一端進入,一端輸出。而當隊列滿時,阻塞。BlockingQueue核心方法:1. 放入數據put2. 獲取數據take。常見的兩種Queue:

2.2.2 ArrayBlockingQueue

基于數組實現,在ArrayBlockingQueue內部,維護了一個定長數組,以便緩存隊列中的數據對象,這是一個常用的阻塞隊列,除了一個定長數組外,ArrayBlockingQueue內部還保存著兩個整形變量,分別標識著隊列的頭部和尾部在數組中的位置。

一段代碼來驗證一下:   

  1.  package map;  
  2.     import java.util.concurrent.*;  
  3.     public class MyTestMap {  
  4.         // 定義阻塞隊列大小  
  5.         private static final int maxSize = 5 
  6.         public static void main(String[] args){  
  7.             ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(maxSize);  
  8.             new Thread(new Productor(queue)).start();  
  9.             new Thread(new Customer(queue)).start();  
  10.         } 
  11.     }  
  12.     class Customer implements Runnable {  
  13.         private BlockingQueue<Integer> queue;  
  14.         Customer(BlockingQueue<Integer> queue) {  
  15.             this.queue = queue;  
  16.         }  
  17.         @Override  
  18.         public void run() {  
  19.             this.cusume();  
  20.         }  
  21.         private void cusume() {  
  22.             while (true) {  
  23.                 try {  
  24.                     int count = (int) queue.take();  
  25.                     System.out.println("customer正在消費第" + count + "個商品===");  
  26.                     // 只是為了方便觀察輸出結果  
  27.                     Thread.sleep(10);  
  28.                 } catch (InterruptedException e) {  
  29.                     e.printStackTrace();  
  30.                 }  
  31.             }  
  32.         }  
  33.     }  
  34.     class Productor implements Runnable {  
  35.         private BlockingQueue<Integer> queue;  
  36.         private int count = 1 
  37.         Productor(BlockingQueue<Integer> queue) {  
  38.             this.queue = queue;  
  39.         }  
  40.         @Override  
  41.         public void run() {  
  42.             this.product();  
  43.         }  
  44.         private void product() {  
  45.             while (true) {  
  46.                 try {  
  47.                     queue.put(count);  
  48.                     System.out.println("生產者正在生產第" + count + "個商品");  
  49.                     count++;  
  50.                 } catch (InterruptedException e) {  
  51.                     e.printStackTrace();  
  52.                 }  
  53.             }  
  54.         }  
  55.     }   
  56.  
  57. //輸出如下  
  58. /**  
  59. 生產者正在生產第1個商品  
  60. 生產者正在生產第2個商品  
  61. 生產者正在生產第3個商品  
  62. 生產者正在生產第4個商品  
  63. 生產者正在生產第5個商品  
  64. customer正在消費第1個商品===  
  65. */ 

2.2.3 LinkedBlockingQueue

基于鏈表的阻塞隊列,內部也維護了一個數據緩沖隊列。需要我們注意的是如果構造一個LinkedBlockingQueue對象,而沒有指定其容量大小。

LinkedBlockingQueue會默認一個類似無限大小的容量(Integer.MAX_VALUE),這樣的話,如果生產者的速度一旦大于消費者的速度,也許還沒有等到隊列滿阻塞產生,系統內存就有可能已被消耗殆盡了。

2.2.4 LinkedBlockingQueue和ArrayBlockingQueue的主要區別

  •  ArrayBlockingQueue的初始化必須傳入隊列大小,LinkedBlockingQueue則可以不傳入。
  •  ArrayBlockingQueue用一把鎖控制并發,LinkedBlockingQueue倆把鎖控制并發,鎖的細粒度更細。即前者生產者消費者進出都是一把鎖,后者生產者生產進入是一把鎖,消費者消費是另一把鎖。
  •  ArrayBlockingQueue采用數組的方式存取,LinkedBlockingQueue用Node鏈表方式存取。

2.2.5handler拒絕策略

Java提供了4種丟棄處理的方法,當然你也可以自己實現,主要是要實現接口:RejectedExecutionHandler中的方法。

  •  AbortPolicy:不處理,直接拋出異常。
  •  CallerRunsPolicy:只用調用者所在線程來運行任務,即提交任務的線程。
  •  DiscardOldestPolicy:LRU策略,丟棄隊列里最近最久不使用的一個任務,并執行當前任務。
  •  DiscardPolicy:不處理,丟棄掉,不拋出異常。

2.2.6線程池五種狀態   

  1. private static final int RUNNING    = -1 << COUNT_BITS 
  2.    private static final int SHUTDOWN   =  0 << COUNT_BITS 
  3.    private static final int STOP       =  1 << COUNT_BITS 
  4.    private static final int TIDYING    =  2 << COUNT_BITS 
  5.    private static final int TERMINATED =  3 << COUNT_BITS

RUNNING:在這個狀態的線程池能判斷接受新提交的任務,并且也能處理阻塞隊列中的任務。

SHUTDOWN:處于關閉的狀態,該線程池不能接受新提交的任務,但是可以處理阻塞隊列中已經保存的任務,在線程處于RUNNING狀態,調用shutdown()方法能切換為該狀態。

STOP:線程池處于該狀態時既不能接受新的任務也不能處理阻塞隊列中的任務,并且能中斷現在線程中的任務。當線程處于RUNNING和SHUTDOWN狀態,調用shutdownNow()方法就可以使線程變為該狀態。

TIDYING:在SHUTDOWN狀態下阻塞隊列為空,且線程中的工作線程數量為0就會進入該狀態,當在STOP狀態下時,只要線程中的工作線程數量為0就會進入該狀態。

TERMINATED:在TIDYING狀態下調用terminated()方法就會進入該狀態。可以認為該狀態是最終的終止狀態。

回到線程池創建ThreadPoolExecutor,我們了解了這些參數,再來看看ThreadPoolExecutor的內部工作原理:

  •  判斷核心線程是否已滿,是進入隊列,否:創建線程
  •  判斷等待隊列是否已滿,是:查看線程池是否已滿,否:進入等待隊列
  •  查看線程池是否已滿,是:拒絕,否創建線程

2.3深入理解ThreadPoolExecutor

進入Execute方法可以看到: 

  1. public void execute(Runnable command) {  
  2.        if (command == null)  
  3.            throw new NullPointerException();  
  4.        int c = ctl.get();  
  5.      //判斷當前活躍線程數是否小于corePoolSize,如果小于,則調用addWorker創建線程執行任務  
  6.        if (workerCountOf(c) < corePoolSize) {  
  7.            if (addWorker(command, true))  
  8.                return;  
  9.            c = ctl.get();  
  10.        }  
  11.      //如果不小于corePoolSize,則將任務添加到workQueue隊列。  
  12.        if (isRunning(c) && workQueue.offer(command)) {  
  13.            int recheck = ctl.get();  
  14.            if (! isRunning(recheck) && remove(command))  
  15.                reject(command);  
  16.            else if (workerCountOf(recheck) == 0)  
  17.                addWorker(null, false);  
  18.        }  
  19.      //如果放入workQueue失敗,則創建線程執行任務,如果這時創建線程失敗(當前線程數不小于maximumPoolSize時),就會調用reject(內部調用handler)拒絕接受任務。  
  20.        else if (!addWorker(command, false))  
  21.            reject(command);  
  22.    } 

AddWorker方法:

  •  創建Worker對象,同時也會實例化一個Thread對象。在創建Worker時會調用threadFactory來創建一個線程。
  •  然后啟動這個線程。

2.3.1線程池中CTL屬性的作用是什么?

看源碼第一反應就是這個CTL到底是個什么東東?有啥用?一番研究得出如下結論:

CTL屬性包含兩個概念: 

  1. private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));  
  2.   private static int ctlOf(int rs, int wc) { return rs | wc; } 
  •  runState:即rs 表明當前線程池的狀態,是否處于Running,Shutdown,Stop,Tidying。
  •  workerCount:即wc表明當前有效的線程數。

我們點擊workerCount即工作狀態記錄值,以RUNNING為例,RUNNING = -1 << COUNT_BITS;,即-1無符號左移COUNT_BITS位,進一步我們得知COUNT_BITS位29,因為Integer位數為31位(2的五次方減一) 

  1. private static final int COUNT_BITS = Integer.SIZE - 3; 

既然是29位那么就是Running的值為: 

  1. 1110 0000 0000 0000 0000 0000 0000 0000   
  2. |||  
  3. 31~29位 

那低28位呢,就是記錄當前線程的總線數啦: 

  1. // Packing and unpacking ctl  
  2.   private static int runStateOf(int c)     { return c & ~CAPACITY; }  
  3.   private static int workerCountOf(int c)  { return c & CAPACITY; }  
  4.   private static int ctlOf(int rs, int wc) { return rs | wc; } 

從上述代碼可以看到workerCountOf這個函數傳入ctl之后,是通過CTL&CAPACITY操作來獲取當前運行線程總數的。

也就是RunningState|WorkCount&CAPACITY,算出來的就是低28位的值。因為CAPACITY得到的就是高3位(29-31位)位0,低28位(0-28位)都是1,所以得到的就是ctl中低28位的值。

而runStateOf這個方法的話,算的就是RunningState|WorkCount&CAPACITY,高3位的值,因為CAPACITY是CAPACITY的取反,所以得到的就是高3位(29-31位)為1,低28位(0-28位)為0,所以通過&運算后,所得到的值就是高3為的值。

簡單來說就是ctl中是高3位作為狀態值,低28位作為線程總數值來進行存儲。

2.3.2 shutdownNow和shutdown的區別

看源碼發現有兩種近乎一樣的方法,shutdownNow和shutdown,設計者這么設計自然是有它的道理,那么這兩個方法的區別在哪呢?

  •  shutdown會把線程池的狀態改為SHUTDOWN,而shutdownNow把當前線程池狀態改為STOP。
  •  shutdown只會中斷所有空閑的線程,而shutdownNow會中斷所有的線程。
  •  shutdown返回方法為空,會將當前任務隊列中的所有任務執行完畢;而shutdownNow把任務隊列中的所有任務都取出來返回。

2.3.3 線程復用原理 

  1. final void runWorker(Worker w) {  
  2.         Thread wt = Thread.currentThread();  
  3.         Runnable task = w.firstTask;  
  4.         w.firstTask = null 
  5.         w.unlock(); // allow interrupts  
  6.         boolean completedAbruptly = true 
  7.         try {  
  8.             while (task != null || (task = getTask()) != null) {  
  9.                 w.lock();  
  10.                 // If pool is stopping, ensure thread is interrupted;  
  11.                 // if not, ensure thread is not interrupted.  This  
  12.                 // requires a recheck in second case to deal with  
  13.                 // shutdownNow race while clearing interrupt  
  14.                 if ((runStateAtLeast(ctl.get(), STOP) ||  
  15.                      (Thread.interrupted() &&  
  16.                       runStateAtLeast(ctl.get(), STOP))) &&  
  17.                     !wt.isInterrupted())  
  18.                     wt.interrupt();  
  19.                 try {  
  20.                     beforeExecute(wt, task);  
  21.                     Throwable thrown = null 
  22.                     try {  
  23.                         task.run();  
  24.                     } catch (RuntimeException x) {  
  25.                         thrown = x; throw x;  
  26.                     } catch (Error x) {  
  27.                         thrown = x; throw x;  
  28.                     } catch (Throwable x) {  
  29.                         thrown = x; throw new Error(x);  
  30.                     } finally {  
  31.                         afterExecute(task, thrown);  
  32.                     }  
  33.                 } finally {  
  34.                     task = null 
  35.                     w.completedTasks++;  
  36.                     w.unlock();  
  37.                 }  
  38.             }  
  39.             completedAbruptly = false 
  40.         } finally {  
  41.             processWorkerExit(w, completedAbruptly);  
  42.         }  
  43.     } 

就是任務在并不只執行創建時指定的firstTask第一任務,還會從任務隊列的中自己主動取任務執行,而且是有或者無時間限定的阻塞等待,以保證線程的存活。

默認的是不允許。

2.4 CountDownLatch和CyclicBarrier區別

countDownLatch是一個計數器,線程完成一個記錄一個,計數器遞減,只能只用一次。

CyclicBarrier的計數器更像一個閥門,需要所有線程都到達,然后繼續執行,計數器遞增,提供Reset功能,可以多次使用。

3. 多線程間通信的幾種方式

提及多線程又不得不提及多線程通信的機制。首先,要短信線程間通信的模型有兩種:共享內存和消息傳遞,以下方式都是基本這兩種模型來實現的。我們來基本一道面試常見的題目來分析:

題目:有兩個線程A、B,A線程向一個集合里面依次添加元素"abc"字符串,一共添加十次,當添加到第五次的時候,希望B線程能夠收到A線程的通知,然后B線程執行相關的業務操作。

3.1使用volatile關鍵字 

  1. package thread;   
  2. /**  
  3.  *   
  4.  * @author hxz  
  5.  * @description 多線程測試類  
  6.  * @version 1.0  
  7.  * @data 2020年2月15日 上午9:10:09  
  8.  */  
  9. public class MyThreadTest {  
  10.     public static void main(String[] args) throws Exception {  
  11.         notifyThreadWithVolatile();  
  12.     }  
  13.     /**  
  14.      * 定義一個測試  
  15.      */  
  16.     private static volatile boolean flag = false 
  17.     /**  
  18.      * 計算I++,當I==5時,通知線程B  
  19.      * @throws Exception  
  20.      */  
  21.     private static void notifyThreadWithVolatile() throws Exception {  
  22.         Thread thc = new Thread("線程A"){  
  23.             @Override  
  24.             public void run() {  
  25.                 for (int i = 0; i < 10; i++) {  
  26.                     if (i == 5) {  
  27.                         flag = true 
  28.                         try {  
  29.                             Thread.sleep(500L);  
  30.                         } catch (InterruptedException e) { 
  31.                             // TODO Auto-generated catch block  
  32.                             e.printStackTrace();  
  33.                         }  
  34.                         break;  
  35.                     }  
  36.                     System.out.println(Thread.currentThread().getName() + "====" + i);  
  37.                 }  
  38.             }  
  39.         };  
  40.         Thread thd = new Thread("線程B") {  
  41.             @Override  
  42.             public void run() {  
  43.                 while (true) {  
  44.                     // 防止偽喚醒 所以使用了while  
  45.                     while (flag) {  
  46.                         System.out.println(Thread.currentThread().getName() + "收到通知");  
  47.                         System.out.println("do something");  
  48.                         try {  
  49.                             Thread.sleep(500L);  
  50.                         } catch (InterruptedException e) {  
  51.                             // TODO Auto-generated catch block  
  52.                             e.printStackTrace();  
  53.                         }  
  54.                         return ;  
  55.                     }  
  56.                 }  
  57.             }  
  58.         };  
  59.         thd.start();  
  60.         Thread.sleep(1000L);  
  61.         thc.start();  
  62.     }  

個人認為這是基本上最好的通信方式,因為A發出通知B能夠立馬接受并Do Something。 

 

責任編輯:龐桂玉 來源: JAVA高級架構
相關推薦

2025-07-11 00:57:30

2010-03-16 17:30:14

Java多線程編程

2022-09-20 19:11:40

HikariCPSpring數據庫

2024-06-06 08:50:43

2018-02-07 08:32:42

2017-10-25 09:31:27

Python運維開發Flask框架

2020-04-20 10:33:56

戴爾

2018-04-26 08:40:33

線性回歸算法機器學習

2024-06-24 08:24:57

2024-08-05 09:05:44

2010-03-17 19:24:38

Java多線程循環

2009-03-12 10:52:43

Java線程多線程

2018-05-11 09:29:18

云計算火鍋IaaS

2010-03-17 09:33:30

Java多線程方案

2020-04-02 15:39:51

代碼編譯器前端

2021-12-26 18:22:30

Java線程多線程

2009-06-29 17:49:47

Java多線程

2022-06-19 22:54:08

TypeScript泛型工具

2019-11-27 14:56:35

關機電腦硬件

2010-02-04 10:19:39

C++多線程
點贊
收藏

51CTO技術棧公眾號

亚洲一二三级电影| 亚洲激情午夜| 欧美日韩免费一区二区三区| 亚洲国产另类久久久精品极度| 天天综合久久综合| 欧美韩国日本在线观看 | av网站中文字幕| 精品国产精品国产偷麻豆| 欧美日韩三级视频| 成年人网站国产| 国产小视频在线播放| 久久国产精品色婷婷| 久久久久国产精品一区| 新91视频在线观看| 国产片侵犯亲女视频播放| 亚洲图片欧美在线| 亚洲精品婷婷| www.国产一区| 国产精品久久久免费观看| 黄色精品视频网站| 性久久久久久久久久久久| 五月天久久综合网| 色综合视频在线| 久久精品国产亚洲高清剧情介绍| 97欧美精品一区二区三区| 91视频免费在观看| 北条麻妃一区二区三区在线| 欧美日韩免费观看一区三区| 大j8黑人w巨大888a片| 国精产品一区| 国产欧美一区二区精品性| 国产精品高清一区二区三区| 一区二区视频播放| 老司机一区二区三区| 欧美激情在线视频二区| 男女全黄做爰文章| 妖精一区二区三区精品视频| 精品美女一区二区三区| jizzzz日本| 欧美精品高清| 欧美午夜美女看片| 超碰成人免费在线| 最新超碰在线| 中文字幕中文字幕在线一区| 欧美一区二区三区四区在线观看地址| 欧美 日韩 国产 成人 在线 91| 卡一卡二国产精品 | 欧美xxx性| 亚洲国产毛片aaaaa无费看| 伊人久久大香线蕉午夜av| 国产裸舞福利在线视频合集| 91性感美女视频| 国产视频一区二区不卡| 丰满人妻一区二区三区免费| 国产精品1区2区3区在线观看| 成人免费福利在线| 91精品在线视频观看| 麻豆国产精品官网| 国产综合视频在线观看| 亚洲一二区视频| 久久精品国产一区二区三 | 成人av电影在线| 99re在线国产| 亚洲欧美国产高清va在线播放| 国产一区二区在线影院| 亚洲一区二区三区在线视频| 国产婷婷在线视频| 国产成人a级片| 国产精品成人一区二区三区| 欧美 日韩 国产 精品| 97久久精品人人做人人爽50路| 久久99影院| 暖暖视频在线免费观看| 国产欧美综合在线观看第十页| 日韩欧美99| 二区三区在线播放| 国产精品福利影院| 波多野结衣 作品| f2c人成在线观看免费视频| 精品久久久一区二区| 日韩中文字幕二区| 欧美性www| 日韩欧美国产精品一区| 国产高潮视频在线观看| 亚洲8888| 久久精品青青大伊人av| 久一区二区三区| 久久国产福利| 亚洲一区二区少妇| 天天色综合久久| 国产欧美一区二区精品忘忧草| 国产一二三四五| 国产在线天堂www网在线观看| 日本丰满少妇一区二区三区| 91香蕉视频污版| 亚洲高清999| 亚洲精品视频久久| 无码少妇一区二区| 91精品婷婷色在线观看| 午夜精品一区二区三区av| 中文资源在线播放| 成人美女视频在线观看| 品久久久久久久久久96高清| 久久亚洲天堂| 色综合久久中文字幕| 永久免费黄色片| 少妇精品久久久一区二区| 欧美另类精品xxxx孕妇| 日本成人一级片| 大白屁股一区二区视频| 亚洲一区高清| 小h片在线观看| 欧美一区二区三区思思人| 波多野结衣福利| 99久久婷婷国产综合精品电影√| 91精品国产91久久久久久不卡| 中国老头性行为xxxx| av亚洲精华国产精华精| 中文字幕精品一区日韩| 黑人巨大精品| 亚洲成人精品av| 日本中文在线视频| 日本美女一区二区三区| 国内一区在线| 三级网站视频在在线播放| 欧美中文字幕不卡| 爱爱免费小视频| 狠狠综合久久| 亚洲最大成人在线| 9191在线观看| 91国偷自产一区二区开放时间| 亚洲一级Av无码毛片久久精品| 清纯唯美日韩| 国产999精品久久久| 午夜黄色小视频| 亚洲高清免费视频| 日韩精品国产一区| 夜间精品视频| 国产区精品视频| 天堂中文а√在线| 91久久精品一区二区三| 成人免费看aa片| 亚洲欧美日韩综合国产aⅴ| 电影午夜精品一区二区三区| 国产原创视频在线观看| 欧美久久一区二区| 91香蕉国产视频| 蜜臀av亚洲一区中文字幕| 日本公妇乱淫免费视频一区三区| 亚洲插插视频| 日韩国产一区三区| 日韩在线视频免费播放| 99re这里都是精品| 午夜肉伦伦影院| 亚洲丝袜美腿一区| 国产成人短视频| 成av人电影在线观看| 欧美性色黄大片| 国产7777777| 精品一区二区三区在线观看 | 日本久久一区二区三区| 日韩精品电影一区二区| 丝袜美腿亚洲一区二区图片| 欧洲亚洲一区| 在线欧美激情| 欧美精品免费播放| 日韩专区第一页| 一本久久精品一区二区| 中文字幕黄色网址| 极品美女销魂一区二区三区| 国产成人生活片| 动漫av一区| 日韩av男人的天堂| 3p在线观看| 日韩一卡二卡三卡四卡| 久久综合久久鬼| 久久久一区二区三区捆绑**| 日日噜噜噜噜久久久精品毛片| 国产精品久久久久一区二区三区厕所 | 国产亚洲精品久久久久久无几年桃 | 一区二区三区欧美亚洲| 国产精久久久久| 亚洲一区免费| 一区二区三区|亚洲午夜| 青草综合视频| 欧美激情中文网| 欧美人体大胆444www| 欧美日韩一本到| 国产污视频在线看| 国产亚洲午夜高清国产拍精品| 伊人国产在线视频| 国产一区日韩欧美| 午夜精品一区二区在线观看| 高清国产一区二区三区四区五区| 欧美精品久久久久| 福利在线观看| 精品国产髙清在线看国产毛片| 久久精品一二区| 日韩久久一区二区| 黄色国产在线观看| 极品美女销魂一区二区三区| 黄页免费在线观看视频| 久久精品国产68国产精品亚洲| 国产精品伊人日日| 欧美91在线|欧美| 91av在线免费观看视频| 欧美成人性生活视频| 日韩国产欧美精品一区二区三区| 一区二区美女视频| 精品久久久免费| 国产va在线播放| 国产女主播一区| 日本不卡视频一区| 蜜桃av一区二区三区| 亚洲 欧美 日韩 国产综合 在线| 日韩一区二区在线免费| 国产精品综合久久久久久| 日本亚洲欧洲无免费码在线| 91av在线看| 啦啦啦中文在线观看日本| 国产一区二区三区在线观看视频| 二区三区在线视频| 欧美丰满嫩嫩电影| 一级黄色在线观看| 欧美日韩国产限制| 久久精品国产亚洲av香蕉| 国产精品网曝门| 最新中文字幕视频| 99精品视频在线观看免费| 91女神在线观看| 日韩二区三区在线观看| 黑人糟蹋人妻hd中文字幕 | 中国av一区二区三区| 国产不卡一二三| 国产91精品一区二区麻豆网站| 欧美日韩精品区别| 免费观看成人鲁鲁鲁鲁鲁视频| 国产中文字幕视频在线观看| 黄色成人精品网站| 国产玉足脚交久久欧美| 中文精品久久| 国产盗摄视频在线观看| 国产精品麻豆久久| 一本色道久久99精品综合| 日本欧美肥老太交大片| 天堂精品视频| 欧美伦理影院| 亚洲欧美精品| 日韩欧美高清| 亚洲综合五月天| 国产精品99久久精品| 亚洲一区二区精品在线| av中文字幕在线| 欧美ab在线视频| 欧美日产一区二区三区在线观看| 99这里只有精品视频| 99免费在线观看视频| 亚洲一区电影| 国产传媒欧美日韩| 精品资源在线| 久久婷婷人人澡人人喊人人爽| 日韩成人av在线资源| 蜜桃91精品入口| 国产一区二区三区探花| 日韩一本精品| 色综合久久网| 最新av在线免费观看| 欧美久久一区| 国产一级爱c视频| 久久不射网站| 亚洲一区在线不卡| 久久99久久精品| 伊人av在线播放| a级精品国产片在线观看| 亚洲第一成人网站| 中国色在线观看另类| 男女性高潮免费网站| 亚洲一区二区美女| 中文字幕激情小说| 欧美亚洲愉拍一区二区| 国产人妖在线播放| 亚洲国产毛片完整版| 极品白浆推特女神在线观看| 在线观看久久久久久| 91精品久久| 欧美在线视频免费播放| 久草综合在线| 高清av免费一区中文字幕| 男男gay无套免费视频欧美 | 色97色成人| 91免费网站视频| 在线亚洲自拍| 精品亚洲一区二区三区四区| 国产+成+人+亚洲欧洲自线| 99久久久久久久久久| 国产精品国产三级国产有无不卡 | 羞羞色午夜精品一区二区三区| 黄色特一级视频| 久久午夜视频| 在线观看免费看片| 久久一日本道色综合| 中文字幕人妻一区二| 一本到高清视频免费精品| 国产女人高潮的av毛片| 亚洲摸下面视频| av在线free| 国产成人avxxxxx在线看| 久久三级中文| 色婷婷精品国产一区二区三区| 国产精品99免费看| 在线看的黄色网址| 国产.欧美.日韩| 国产中文字幕久久| 欧美色xxxx| 亚洲爱情岛论坛永久| 国产一区二区三区在线观看视频| 欧美韩日亚洲| 成人写真视频福利网| 国产欧美日韩精品一区二区三区 | 久久国产高清| 伊人久久一区二区三区| 中文字幕在线播放不卡一区| 欧美精品韩国精品| 精品国产免费一区二区三区四区| 日本中文字幕伦在线观看| 日韩免费高清在线观看| 超碰地址久久| 97av中文字幕| 国产精品69久久久久水密桃| 国产三级aaa| 欧美系列一区二区| 欧美扣逼视频| 欧美一区亚洲一区| 欧美大胆a级| 日韩黄色短视频| 懂色av中文字幕一区二区三区| 中文字幕观看av| 欧美三级日本三级少妇99| 精品资源在线看| 国产99久久精品一区二区 夜夜躁日日躁 | 亚洲看片一区| 国产性猛交96| 一区二区三区四区五区视频在线观看| 亚洲天堂视频在线| 正在播放亚洲1区| 免费污视频在线一区| 欧美日本韩国在线| 视频一区二区欧美| 中文字幕免费视频| 在线精品视频免费播放| 黄色av免费在线观看| 日韩av片永久免费网站| 亚州av一区| 成人中文字幕av| 国产欧美中文在线| 中文字幕在线观看1| 中文字幕日韩在线观看| 日韩第二十一页| 中国一级黄色录像| 成人在线综合网| 久久久久久久久久影院| 日韩av一区在线观看| 电影一区二区三区| 日本欧美精品久久久| 蜜桃精品视频在线观看| 中文字幕无码日韩专区免费| 91精品国产91热久久久做人人| 中文在线手机av| 国产日韩欧美一区二区三区四区| 夜夜嗨一区二区三区| 国产jk精品白丝av在线观看| 欧美日韩精品一区二区天天拍小说| 男人资源在线播放| 444亚洲人体| 亚洲国产精品第一区二区三区| 性久久久久久久久久| 欧美性受xxxx黑人xyx| a级网站在线播放| 国产一区二区精品在线| 久久亚洲一区| 波多野结衣喷潮| 亚洲成人av在线播放| 345成人影院| 午夜啪啪免费视频| 成人国产精品免费观看视频| 国产精品久久久久久久久夜色| 久久精品国产成人精品| 超碰97久久国产精品牛牛| 国产又大又黄又粗的视频| 亚洲美女视频在线观看| 天堂av在线播放| 91久久在线视频| 国产精品日韩久久久| 精品一区二区在线观看视频| 精品国产髙清在线看国产毛片| 国产成人精品一区二三区在线观看 | 午夜久久久久久电影| 免费av在线电影| 2019国产精品视频| 日韩高清一级片|