<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java JUC總結

          共 35045字,需瀏覽 71分鐘

           ·

          2021-03-26 10:41

          點擊上方藍色字體,選擇“標星公眾號”

          優(yōu)質文章,第一時間送達

          76套java從入門到精通實戰(zhàn)課程分享

          一、Java JUC簡介

          在 Java 5.0 提供了 java.util.concurrent (簡稱JUC )包,在此包中增加了在并發(fā)編程中很常用的實用工具類,用于定義類似于線程的自定義子系統,包括線程池、異步 IO 和輕量級任務框架。提供可調的、靈活的線程池。還提供了設計用于多線程上下文中的 Collection 實現等。


          二、內存可見性 、volatile關鍵字

          1. 內存可見性

          • 內存可見性(Memory Visibility)是指當某個線程正在使用對象狀態(tài)而另一個線程在同時修改該狀態(tài),需要確保當一個線程修改了對象狀態(tài)后,其他線程能夠看到發(fā)生的狀態(tài)變化。

          • 可見性錯誤是指當讀操作與寫操作在不同的線程中執(zhí)行時,我們無法確保執(zhí)行讀操作的線程能適時地看到其他線程寫入的值,有時甚至是根本不可能的事情。

          • 我們可以通過同步來保證對象被安全地發(fā)布。除此之外我們也可以使用一種更加輕量級的 volatile變量。

          2. volatile 關鍵字

          Java 提供了一種稍弱的同步機制,即 volatile 變量,用來確保將變量的更新操作通知到其他線程。可以將 volatile 看做一個輕量級的鎖,但是又與鎖有些不同:

          • 對于多線程,不是一種互斥關系

          • 不能保證變量狀態(tài)的“原子性操作”

          - 原子性操作解釋

          例如 i++; 這個操作,它不是一個原子性操作,在實際執(zhí)行時需要三步操作“讀-改-寫”:

          int temp = i;
          temp = temp + 1;
          i = temp; 

          示例代碼

          public class TestVolatile {
           
           public static void main(String[] args) {
            ThreadDemo td = new ThreadDemo();
            new Thread(td).start();
            
            while(true){
             if(td.isFlag()){
              System.out.println("------------------");
              break;
             }
            }
            
           }

          }

          class ThreadDemo implements Runnable {

           private volatile boolean flag = false;

           @Override
           public void run() {
            
            try {
             Thread.sleep(200);
            } catch (InterruptedException e) {
            }

            flag = true;
            
            System.out.println("flag=" + isFlag());

           }

           public boolean isFlag() {
            return flag;
           }

          }

          三、CAS算法、原子變量

          1. CAS算法

          • CAS (Compare-And-Swap) 是一種硬件對并發(fā)的支持,針對多處理器操作而設計的處理器中的一種特殊指令,用于管理對共享數據的并發(fā)訪問。

          • CAS 是一種無鎖的非阻塞算法的實現。

          • CAS 包含了 3 個操作數:需要讀寫的內存值 V、進行比較的值 A、擬寫入的新值 B

          • 當且僅當 V 的值等于 A 時,CAS 通過原子方式用新值 B 來更新 V 的值,否則不會執(zhí)行任何操作。

          模擬CAS算法

          public class TestCompareAndSwap {

           public static void main(String[] args) {
            final CompareAndSwap cas = new CompareAndSwap();
            
            for (int i = 0; i < 10; i++) {
             new Thread(new Runnable() {
              
              @Override
              public void run() {
               int expectedValue = cas.get();
               boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101));
               System.out.println(b);
              }
             }).start();
            }
           }
          }

          class CompareAndSwap{
           private int value;
           
           //獲取內存值
           public synchronized int get(){
            return value;
           }
           
           //比較
           public synchronized int compareAndSwap(int expectedValue, int newValue){
            int oldValue = value;
            
            if(oldValue == expectedValue){
             this.value = newValue;
            }
            
            return oldValue;
           }
           
           //設置
           public synchronized boolean compareAndSet(int expectedValue, int newValue){
            return expectedValue == compareAndSwap(expectedValue, newValue);
           }
          }

          2. 原子變量

          • 類的小工具包,支持在單個變量上解除鎖的線程安全編程。事實上,此包中的類可將 volatile 值、字段和數組元素的概念擴展到那些也提供原子條件更新操作的。

          • 類 AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference 的實例各自提供對相應類型單個變量的訪問和更新。每個類也為該類型提供適當的實用工具方法。

          • AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray 類進一步擴展了原子操作,對這些類型的數組提供了支持。這些類在為其數組元素提供 volatile 訪問語義方面也引人注目,這對于普通數組來說是不受支持的。

          • 核心方法:boolean compareAndSet(expectedValue, updateValue)

          • java.util.concurrent.atomic 包下提供了一些原子操作的常用類:

                (1)AtomicBoolean 、AtomicInteger 、AtomicLong 、 AtomicReference

                (2)AtomicIntegerArray 、AtomicLongArray

                (3)AtomicMarkableReference

                (4)AtomicReferenceArray

                (5)AtomicStampedReference

          示例代碼

          import java.util.concurrent.atomic.AtomicInteger;

          public class TestAtomicDemo {

           public static void main(String[] args) {
            AtomicDemo ad = new AtomicDemo();
            
            for (int i = 0; i < 10; i++) {
             new Thread(ad).start();
            }
           }
           
          }

          class AtomicDemo implements Runnable{
           
          // private volatile int serialNumber = 0;
           
           private AtomicInteger serialNumber = new AtomicInteger(0);

           @Override
           public void run() {
            
            try {
             Thread.sleep(200);
            } catch (InterruptedException e) {
            }
            
            System.out.println(getSerialNumber());
           }
           
           public int getSerialNumber(){
            return serialNumber.getAndIncrement(); // 原子執(zhí)行自增1操作
           }
          }

          四、ConcurrentHashMap 鎖分段機制

          • Java 5.0 在 java.util.concurrent 包中提供了多種并發(fā)容器類來改進同步容器的性能。

          • ConcurrentHashMap 同步容器類是Java 5 增加的一個線程安全的哈希表。對與多線程的操作,介于 HashMap 與 Hashtable 之間。內部采用“鎖分段”機制替代 Hashtable 的獨占鎖。進而提高性能。

          • 此包還提供了設計用于多線程上下文中的 Collection 實現:ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、CopyOnWriteArrayList 和CopyOnWriteArraySet。當期望許多線程訪問一個給定 collection 時,ConcurrentHashMap 通常優(yōu)于同步的 HashMap,ConcurrentSkipListMap 通常優(yōu)于同步的 TreeMap。當期望的讀數和遍歷遠遠大于列表的更新數時,CopyOnWriteArrayList 優(yōu)于同步的 ArrayList。

          示例代碼

          import java.util.Iterator;
          import java.util.concurrent.CopyOnWriteArrayList;

          /*
           * CopyOnWriteArrayList/CopyOnWriteArraySet : “寫入并復制”
           * 注意:添加操作多時,效率低,因為每次添加時都會進行復制,開銷非常的大。并發(fā)迭代操作多時可以選擇。
           */
          public class TestCopyOnWriteArrayList {

           public static void main(String[] args) {
            HelloThread ht = new HelloThread();
            
            for (int i = 0; i < 10; i++) {
             new Thread(ht).start();
            }
           }
          }

          class HelloThread implements Runnable{
           
          // private static List<String> list = Collections.synchronizedList(new ArrayList<String>());
           
           private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
           
           static{
            list.add("AA");
            list.add("BB");
            list.add("CC");
           }

           @Override
           public void run() {
            Iterator<String> it = list.iterator();
            
            while(it.hasNext()){
             System.out.println(it.next());
             
             list.add("AA");
            }
           }
          }

          五、CountDownLatch 閉鎖

          • Java 5.0 在 java.util.concurrent 包中提供了多種并發(fā)容器類來改進同步容器的性能。

          • CountDownLatch 是一個同步輔助類,在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個或多個線程一直等待。

          • 閉鎖可以延遲線程的進度直到其到達終止狀態(tài),閉鎖可以用來確保某些活動直到其他活動都完成才繼續(xù)執(zhí)行:

                 (1)確保某個計算在其需要的所有資源都被初始化之后才繼續(xù)執(zhí)行;

                 (2)確保某個服務在其依賴的所有其他服務都已經啟動之后才啟動;

                 (3)等待直到某個操作所有參與者都準備就緒再繼續(xù)執(zhí)行。

          示例程序

          import java.util.concurrent.CountDownLatch;

          /**
           * CountDownLatch:閉鎖,在完成某些運算時,只有其他所有線程的運算全部完成,當前運算才繼續(xù)執(zhí)行
           */
          public class TestCountDownLatch {

              public static void main(String[] agrs) {
                  CountDownLatch latch = new CountDownLatch(5); // 5表示有5個線程
                  LatchDemo ld = new LatchDemo(latch);

                  long start = System.currentTimeMillis();

                  for (int i = 0; i < 5; i++) {
                      new Thread(ld).start();
                  }

                  try {
                      latch.await(); // 等待
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }

                  long end = System.currentTimeMillis();

                  System.out.println("耗費時間為:" + (end - start) + "ms");
              }

          }

          class LatchDemo implements Runnable {
              private CountDownLatch latch;

              public LatchDemo(CountDownLatch latch) {
                  this.latch = latch;
              }

              @Override
              public void run() {
                  try {
                      for (int i = 0; i < 50000; i++) {
                          if (i % 2 == 0) {
                              System.out.println(i);
                          }
                      }
                  } finally { // 必須執(zhí)行的操作
                      latch.countDown();
                  }
              }
          }

          六、Callable 接口

          • Java 5.0 在 java.util.concurrent 提供了一個新的創(chuàng)建執(zhí)行線程的方式:Callable 接口

          • Callable 接口類似于 Runnable,兩者都是為那些其實例可能被另一個線程執(zhí)行的類設計的。但是 Runnable 不會返回結果,并且無法拋出經過檢查的異常。

          • Callable 需要依賴FutureTask ,FutureTask 也可以用作閉鎖。

          示例程序

          import java.util.concurrent.Callable;
          import java.util.concurrent.ExecutionException;
          import java.util.concurrent.FutureTask;

          /**
           * 一、創(chuàng)建執(zhí)行線程的方式三:實現Callable接口。相較于實現Runnable接口的方式,方法可以有返回值,并且可以拋出異常
           * 二、執(zhí)行Callable方式,需要FutureTask實現類的支持,用于接受運算結果。FutureTask是Future接口的實現類
           */
          public class TestCallable {

              public static void main(String[] args) {
                  ThreadDemo td = new ThreadDemo();

                  // 1.執(zhí)行Callable方式,需要FutureTask實現類的支持,用于接受運算結果
                  FutureTask<Integer> result = new FutureTask<>(td);
                  new Thread(result).start();
                  // 2.接收線程運算后的結果
                  try {
                      Integer sum = result.get(); // FutureTask可用于閉鎖
                      System.out.println(sum);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  } catch (ExecutionException e) {
                      e.printStackTrace();
                  }
              }

          }

          class ThreadDemo implements Callable<Integer> {

              @Override
              public Integer call() throws Exception {
                  int sum = 0;
                  for (int i = 0; i <= 100; i++) {
                      sum += i;
                  }
                  return sum;
              }
          }

          七、Lock 同步鎖、Condition 控制線程通信、線程按序交替

          1. 顯示鎖 Lock

          • 在 Java 5.0 之前,協調共享對象的訪問時可以使用的機制只有 synchronized 和 volatile 。Java 5.0 后增加了一些新的機制,但并不是一種替代內置鎖的方法,而是當內置鎖不適用時,作為一種可選擇的高級功能。

          • ReentrantLock 實現了 Lock 接口,并提供了與synchronized 相同的互斥性和內存可見性。但相較于synchronized 提供了更高的處理鎖的靈活性。

          示例代碼

          import java.util.concurrent.locks.Lock;
          import java.util.concurrent.locks.ReentrantLock;

          /*
           * 一、用于解決多線程安全問題的方式:
           * 
           * synchronized:隱式鎖
           * 1. 同步代碼塊
           * 2. 同步方法
           * 
           * jdk 1.5 后:
           * 3. 同步鎖 Lock
           * 注意:是一個顯示鎖,需要通過 lock() 方法上鎖,必須通過 unlock() 方法進行釋放鎖
           */
          public class TestLock {
           
           public static void main(String[] args) {
            Ticket ticket = new Ticket();
            
            new Thread(ticket, "1號窗口").start();
            new Thread(ticket, "2號窗口").start();
            new Thread(ticket, "3號窗口").start();
           }
          }

          class Ticket implements Runnable{ 
           private int tick = 100;
           
           private Lock lock = new ReentrantLock();

           @Override
           public void run() {
            while(true){
             
             lock.lock(); //上鎖
             
             try{
              if(tick > 0){
               try {
                Thread.sleep(200);
               } catch (InterruptedException e) {
               }
               
               System.out.println(Thread.currentThread().getName() + " 完成售票,余票為:" + --tick);
              }
             }finally{
              lock.unlock(); //釋放鎖
             }
            }
           }
          }

          2. Condition 控制線程通信

          • Condition 接口描述了可能會與鎖有關聯的條件變量。這些變量在用法上與使用 Object.wait 訪問的隱式監(jiān)視器類似,但提供了更強大的功能。需要特別指出的是,單個 Lock 可能與多個 Condition 對象關聯。為了避免兼容性問題,Condition 方法的名稱與對應的 Object 版本中的不同。

          • 在 Condition 對象中,與 wait、notify 和 notifyAll 方法對應的分別是await、signal 和 signalAll。

          • Condition 實例實質上被綁定到一個鎖上。要為特定 Lock 實例獲得Condition 實例,請使用其 newCondition() 方法。

          3. 線程按序交替

          • Lock和Condition結合應用以實現線程按序交替。


          案例:

          編寫一個程序,開啟 3 個線程,這三個線程的 ID 分別為A、B、C,每個線程將自己的 ID 在屏幕上打印 10 遍,要求輸出的結果必須按順序顯示。如:ABCABCABC…… 依次遞歸。

          import java.util.concurrent.locks.Condition;
          import java.util.concurrent.locks.Lock;
          import java.util.concurrent.locks.ReentrantLock;

          public class TestABCAlternate {

              public static void main(String[] agrs) {
                  AlternateDemo ad = new AlternateDemo();
                  new Thread(new Runnable() {
                      @Override
                      public void run() {
                          for (int i = 0; i < 10; i++) {
                              ad.loopA();
                          }
                      }
                  }, "A").start();
                  new Thread(new Runnable() {
                      @Override
                      public void run() {
                          for (int i = 0; i < 10; i++) {
                              ad.loopB();
                          }
                      }
                  }, "B").start();
                  new Thread(new Runnable() {
                      @Override
                      public void run() {
                          for (int i = 0; i < 10; i++) {
                              ad.loopC();
                          }
                      }
                  }, "C").start();
              }

          }

          class AlternateDemo {
              private int number = 1; // 當前正在執(zhí)行的線程標記
              private Lock lock = new ReentrantLock();
              private Condition condition1 = lock.newCondition();
              private Condition condition2 = lock.newCondition();
              private Condition condition3 = lock.newCondition();

              public void loopA() {
                  lock.lock();
                  try {
                      // 1.判斷
                      if (number != 1) {
                          condition1.await();
                      }
                      // 2.打印
                      System.out.print(Thread.currentThread().getName());
                      // 3.喚醒
                      number = 2;
                      condition2.signal();
                  } catch (Exception e) {
                      e.printStackTrace();
                  } finally {
                      lock.unlock();
                  }
              }

              public void loopB() {
                  lock.lock();
                  try {
                      if (number != 2) {
                          try {
                              condition2.await();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                      System.out.print(Thread.currentThread().getName());
                      number = 3;
                      condition3.signal();
                  } finally {
                      lock.unlock();
                  }
              }

              public void loopC() {
                  lock.lock();
                  try {
                      if (number != 3) {
                          try {
                              condition3.await();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                      System.out.print(Thread.currentThread().getName());
                      number = 1;
                      condition1.signal();
                  } finally {
                      lock.unlock();
                  }
              }
          }

          八、ReadWriteLock 讀寫鎖

          • ReadWriteLock 維護了一對相關的鎖,一個用于只讀操作,另一個用于寫入操作。只要沒有 writer,讀取鎖可以由多個 reader 線程同時保持。寫入鎖是獨占的。

          • ReadWriteLock 讀取操作通常不會改變共享資源,但執(zhí)行寫入操作時,必須獨占方式來獲取鎖。對于讀取操作占多數的數據結構。ReadWriteLock 能提供比獨占鎖更高的并發(fā)性。而對于只讀的數據結構,其中包含的不變性可以完全不需要考慮加鎖操作。

          示例代碼

          import java.util.concurrent.locks.ReadWriteLock;
          import java.util.concurrent.locks.ReentrantReadWriteLock;

          /*
           * 1. ReadWriteLock : 讀寫鎖
           * 
           * 寫寫/讀寫 需要“互斥”
           * 讀讀 不需要互斥
           * 
           */
          public class TestReadWriteLock {

           public static void main(String[] args) {
            ReadWriteLockDemo rw = new ReadWriteLockDemo();
            
            new Thread(new Runnable() {
             @Override
             public void run() {
              rw.set((int)(Math.random() * 101));
             }
            }, "Write:").start();
            
            for (int i = 0; i < 100; i++) {
             new Thread(new Runnable() {
              
              @Override
              public void run() {
               rw.get();
              }
             }).start();
            }
           }
          }

          class ReadWriteLockDemo{
           private int number = 0;
           
           private ReadWriteLock lock = new ReentrantReadWriteLock();
           
           //讀
           public void get(){
            lock.readLock().lock(); //上鎖
            try{
             System.out.println(Thread.currentThread().getName() + " : " + number);
            }finally{
             lock.readLock().unlock(); //釋放鎖
            }
           }
           
           //寫
           public void set(int number){
            lock.writeLock().lock();
            try{
             System.out.println(Thread.currentThread().getName());
             this.number = number;
            }finally{
             lock.writeLock().unlock();
            }
           }
          }

          九、線程八鎖

          • 一個對象里面如果有多個synchronized方法,某一個時刻內,只要一個線程去調用其中的一個synchronized方法了,其它的線程都只能等待,換句話說,某一個時刻內,只能有唯一一個線程去訪問這些synchronized方法。

          • 鎖的是當前對象this,被鎖定后,其它的線程都不能進入到當前對象的其它的synchronized方法。

          • 加個普通方法后發(fā)現和同步鎖無關。

          • 換成兩個對象后,不是同一把鎖了,情況立刻變化。

          • 都換成靜態(tài)同步方法后,情況又變化。

          總結:

          • 所有的非靜態(tài)同步方法用的都是同一把鎖——實例對象本身,也就是說如果一個實例對象的非靜態(tài)同步方法獲取鎖后,該實例對象的其他非靜態(tài)同步方法必須等待獲取鎖的方法釋放鎖后才能獲取鎖,可是別的實例對象的非靜態(tài)同步方法因為跟該實例對象的非靜態(tài)同步方法用的是不同的鎖,所以毋須等待該實例對象已獲取鎖的非靜態(tài)同步方法釋放鎖就可以獲取他們自己的鎖。

          • 所有的靜態(tài)同步方法用的也是同一把鎖——類對象本身,這兩把鎖是兩個不同的對象,所以靜態(tài)同步方法與非靜態(tài)同步方法之間是不會有競態(tài)條件的。但是一旦一個靜態(tài)同步方法獲取鎖后,其他的靜態(tài)同步方法都必須等待該方法釋放鎖后才能獲取鎖,而不管是同一個實例對象的靜態(tài)同步方法之間,還是不同的實例對象的靜態(tài)同步方法之間,只要它們同一個類的實例對象!

          十、線程池

          • 第四種獲取線程的方法:線程池,一個 ExecutorService,它使用可能的幾個池線程之一執(zhí)行每個提交的任務,通常使用 Executors 工廠方法配置。

          • 線程池可以解決兩個不同問題:由于減少了每個任務調用的開銷,它們通常可以在執(zhí)行大量異步任務時提供增強的性能,并且還可以提供綁定和管理資源(包括執(zhí)行任務集時使用的線程)的方法。每個 ThreadPoolExecutor 還維護著一些基本的統計數據,如完成的任務數。

          • 為了便于跨大量上下文使用,此類提供了很多可調整的參數和擴展鉤子 (hook)。但是,強烈建議程序員使用較為方便的 Executors 工廠方法 :

          (1)Executors.newCachedThreadPool()(無界線程池,可以進行自動線程回收)

          (2)Executors.newFixedThreadPool(int)(固定大小線程池)

          (3)Executors.newSingleThreadExecutor()(單個后臺線程)

          它們均為大多數使用場景預定義了設置。


          示例代碼

          import java.util.ArrayList;
          import java.util.List;
          import java.util.concurrent.Callable;
          import java.util.concurrent.ExecutorService;
          import java.util.concurrent.Executors;
          import java.util.concurrent.Future;

          /*
           * 一、線程池:提供了一個線程隊列,隊列中保存著所有等待狀態(tài)的線程。避免了創(chuàng)建與銷毀額外開銷,提高了響應的速度。
           * 
           * 二、線程池的體系結構:
           *  java.util.concurrent.Executor : 負責線程的使用與調度的根接口
           *   |--**ExecutorService 子接口: 線程池的主要接口
           *    |--ThreadPoolExecutor 線程池的實現類
           *    |--ScheduledExecutorService 子接口:負責線程的調度
           *     |--ScheduledThreadPoolExecutor :繼承 ThreadPoolExecutor, 實現 ScheduledExecutorService
           * 
           * 三、工具類 : Executors 
           * ExecutorService newFixedThreadPool() : 創(chuàng)建固定大小的線程池
           * ExecutorService newCachedThreadPool() : 緩存線程池,線程池的數量不固定,可以根據需求自動的更改數量。
           * ExecutorService newSingleThreadExecutor() : 創(chuàng)建單個線程池。線程池中只有一個線程
           * 
           * ScheduledExecutorService newScheduledThreadPool() : 創(chuàng)建固定大小的線程,可以延遲或定時的執(zhí)行任務。
           */
          public class TestThreadPool {
           
           public static void main(String[] args) throws Exception {
            //1. 創(chuàng)建線程池
            ExecutorService pool = Executors.newFixedThreadPool(5);
            
            List<Future<Integer>> list = new ArrayList<>();
            
            for (int i = 0; i < 10; i++) {
             Future<Integer> future = pool.submit(new Callable<Integer>(){

              @Override
              public Integer call() throws Exception {
               int sum = 0;
               
               for (int i = 0; i <= 100; i++) {
                sum += i;
               }
               
               return sum;
              }
              
             });

             list.add(future);
            }
            
            pool.shutdown();
            
            for (Future<Integer> future : list) {
             System.out.println(future.get());
            }
            
            /*ThreadPoolDemo tpd = new ThreadPoolDemo();
            
            //2. 為線程池中的線程分配任務
            for (int i = 0; i < 10; i++) {
             pool.submit(tpd);
            }
            
            //3. 關閉線程池
            pool.shutdown();*/
           }
           
          // new Thread(tpd).start();
          // new Thread(tpd).start();

          }

          class ThreadPoolDemo implements Runnable{

           private int i = 0;
           
           @Override
           public void run() {
            while(i <= 100){
             System.out.println(Thread.currentThread().getName() + " : " + i++);
            }
           }
           
          }

          十一、線程調度

          1. ScheduledExecutorService

          一個 ExecutorService,可安排在給定的延遲后運行或定期執(zhí)行的命令。


          示例代碼

          public class TestScheduledThreadPool {

           public static void main(String[] args) throws Exception {
            ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
            
            for (int i = 0; i < 5; i++) {
             Future<Integer> result = pool.schedule(new Callable<Integer>(){

              @Override
              public Integer call() throws Exception {
               int num = new Random().nextInt(100);//生成隨機數
               System.out.println(Thread.currentThread().getName() + " : " + num);
               return num;
              }
              
             }, 1, TimeUnit.SECONDS);
             
             System.out.println(result.get());
            }
            
            pool.shutdown();
           }
          }

          十二、ForkJoinPool 分支/合并框架 工作竊取

          1. Fork/Join 框架

          Fork/Join 框架:就是在必要的情況下,將一個大任務,進行拆分(fork)成若干個小任務(拆到不可再拆時),再將一個個的小任務運算的結果進行 join 匯總。

          2. Fork/Join 框架與線程池的區(qū)別

          • 采用 “工作竊取”模式(work-stealing):

                   當執(zhí)行新的任務時它可以將其拆分分成更小的任務執(zhí)行,并將小任務加到線程隊列中,            然后再從一個隨機線程的隊列中偷一個并把它放在自己的隊列中。

          • 相對于一般的線程池實現,fork/join框架的優(yōu)勢體現在對其中包含的任務的處理方式上.在一般的線程池中,如果一個線程正在執(zhí)行的任務由于某些原因無法繼續(xù)運行,那么該線程會處于等待狀態(tài)。而在fork/join框架實現中,如果某個子問題由于等待另外一個子問題的完成而無法繼續(xù)運行。那么處理該子問題的線程會主動尋找其他尚未運行的子問題來執(zhí)行.這種方式減少了線程的等待時間,提高了性能。

          示例代碼

          public class TestForkJoinPool {
           
           public static void main(String[] args) {
            Instant start = Instant.now();
            
            ForkJoinPool pool = new ForkJoinPool();
            
            ForkJoinTask<Long> task = new ForkJoinSumCalculate(0L, 50000000000L);
            
            Long sum = pool.invoke(task);
            
            System.out.println(sum);
            
            Instant end = Instant.now();
            
            System.out.println("耗費時間為:" + Duration.between(start, end).toMillis());//166-1996-10590
           }
           
           //普通串行計算
           @Test
           public void test1(){
            Instant start = Instant.now();
            
            long sum = 0L;
            
            for (long i = 0L; i <= 50000000000L; i++) {
             sum += i;
            }
            
            System.out.println(sum);
            
            Instant end = Instant.now();
            
            System.out.println("耗費時間為:" + Duration.between(start, end).toMillis());//35-3142-15704
           }
           
           //java8 新特性
           @Test
           public void test2(){
            Instant start = Instant.now();
            
            Long sum = LongStream.rangeClosed(0L, 50000000000L)
                  .parallel()
                  .reduce(0L, Long::sum);
            
            System.out.println(sum);
            
            Instant end = Instant.now();
            
            System.out.println("耗費時間為:" + Duration.between(start, end).toMillis());//1536-8118
           }

          }

          class ForkJoinSumCalculate extends RecursiveTask<Long>{

           /**
            * 
            */
           private static final long serialVersionUID = -259195479995561737L;
           
           private long start;
           private long end;
           
           private static final long THURSHOLD = 10000L;  //臨界值
           
           public ForkJoinSumCalculate(long start, long end) {
            this.start = start;
            this.end = end;
           }

           @Override
           protected Long compute() {
            long length = end - start;
            
            if(length <= THURSHOLD){
             long sum = 0L;
             
             for (long i = start; i <= end; i++) {
              sum += i;
             }
             
             return sum;
            }else{
             long middle = (start + end) / 2;
             
             ForkJoinSumCalculate left = new ForkJoinSumCalculate(start, middle); 
             left.fork(); //進行拆分,同時壓入線程隊列
             
             ForkJoinSumCalculate right = new ForkJoinSumCalculate(middle+1, end);
             right.fork(); //
             
             return left.join() + right.join();
            }
           }
          }

          ————————————————

          版權聲明:本文為CSDN博主「頻率coo」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。

          原文鏈接:

          https://blog.csdn.net/qq_40121502/article/details/88219548




          鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布

          ??????

          ??長按上方微信二維碼 2 秒





          感謝點贊支持下哈 

          瀏覽 52
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一级日韩免费观看 | 青娱乐在线视频免费播放 | 先锋影音麻豆 | 波多在线视频 | 国产无码操 |