1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Java JUC總結(jié)

        共 35045字,需瀏覽 71分鐘

         ·

        2021-03-26 10:41

        點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

        優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

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

        一、Java JUC簡(jiǎn)介

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


        二、內(nèi)存可見(jiàn)性 、volatile關(guān)鍵字

        1. 內(nèi)存可見(jiàn)性

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

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

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

        2. volatile 關(guān)鍵字

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

        • 對(duì)于多線程,不是一種互斥關(guān)系

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

        - 原子性操作解釋

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

        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) 是一種硬件對(duì)并發(fā)的支持,針對(duì)多處理器操作而設(shè)計(jì)的處理器中的一種特殊指令,用于管理對(duì)共享數(shù)據(jù)的并發(fā)訪問(wèn)。

        • CAS 是一種無(wú)鎖的非阻塞算法的實(shí)現(xiàn)。

        • CAS 包含了 3 個(gè)操作數(shù):需要讀寫的內(nèi)存值 V、進(jìn)行比較的值 A、擬寫入的新值 B

        • 當(dāng)且僅當(dāng) V 的值等于 A 時(shí),CAS 通過(guò)原子方式用新值 B 來(lái)更新 V 的值,否則不會(huì)執(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;
         
         //獲取內(nèi)存值
         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;
         }
         
         //設(shè)置
         public synchronized boolean compareAndSet(int expectedValue, int newValue){
          return expectedValue == compareAndSwap(expectedValue, newValue);
         }
        }

        2. 原子變量

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

        • 類 AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference 的實(shí)例各自提供對(duì)相應(yīng)類型單個(gè)變量的訪問(wèn)和更新。每個(gè)類也為該類型提供適當(dāng)?shù)膶?shí)用工具方法。

        • AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray 類進(jìn)一步擴(kuò)展了原子操作,對(duì)這些類型的數(shù)組提供了支持。這些類在為其數(shù)組元素提供 volatile 訪問(wèn)語(yǔ)義方面也引人注目,這對(duì)于普通數(shù)組來(lái)說(shuō)是不受支持的。

        • 核心方法: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 鎖分段機(jī)制

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

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

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

        示例代碼

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

        /*
         * CopyOnWriteArrayList/CopyOnWriteArraySet : “寫入并復(fù)制”
         * 注意:添加操作多時(shí),效率低,因?yàn)槊看翁砑訒r(shí)都會(huì)進(jìn)行復(fù)制,開(kāi)銷非常的大。并發(fā)迭代操作多時(shí)可以選擇。
         */
        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ā)容器類來(lái)改進(jìn)同步容器的性能。

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

        • 閉鎖可以延遲線程的進(jìn)度直到其到達(dá)終止?fàn)顟B(tài),閉鎖可以用來(lái)確保某些活動(dòng)直到其他活動(dòng)都完成才繼續(xù)執(zhí)行:

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

               (2)確保某個(gè)服務(wù)在其依賴的所有其他服務(wù)都已經(jīng)啟動(dòng)之后才啟動(dòng);

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

        示例程序

        import java.util.concurrent.CountDownLatch;

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

            public static void main(String[] agrs) {
                CountDownLatch latch = new CountDownLatch(5); // 5表示有5個(gè)線程
                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("耗費(fèi)時(shí)間為:" + (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 提供了一個(gè)新的創(chuàng)建執(zhí)行線程的方式:Callable 接口

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

        • Callable 需要依賴FutureTask ,F(xiàn)utureTask 也可以用作閉鎖。

        示例程序

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

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

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

                // 1.執(zhí)行Callable方式,需要FutureTask實(shí)現(xiàn)類的支持,用于接受運(yùn)算結(jié)果
                FutureTask<Integer> result = new FutureTask<>(td);
                new Thread(result).start();
                // 2.接收線程運(yùn)算后的結(jié)果
                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 之前,協(xié)調(diào)共享對(duì)象的訪問(wèn)時(shí)可以使用的機(jī)制只有 synchronized 和 volatile 。Java 5.0 后增加了一些新的機(jī)制,但并不是一種替代內(nèi)置鎖的方法,而是當(dāng)內(nèi)置鎖不適用時(shí),作為一種可選擇的高級(jí)功能。

        • ReentrantLock 實(shí)現(xiàn)了 Lock 接口,并提供了與synchronized 相同的互斥性和內(nèi)存可見(jiàn)性。但相較于synchronized 提供了更高的處理鎖的靈活性。

        示例代碼

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

        /*
         * 一、用于解決多線程安全問(wèn)題的方式:
         * 
         * synchronized:隱式鎖
         * 1. 同步代碼塊
         * 2. 同步方法
         * 
         * jdk 1.5 后:
         * 3. 同步鎖 Lock
         * 注意:是一個(gè)顯示鎖,需要通過(guò) lock() 方法上鎖,必須通過(guò) unlock() 方法進(jìn)行釋放鎖
         */
        public class TestLock {
         
         public static void main(String[] args) {
          Ticket ticket = new Ticket();
          
          new Thread(ticket, "1號(hào)窗口").start();
          new Thread(ticket, "2號(hào)窗口").start();
          new Thread(ticket, "3號(hào)窗口").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 接口描述了可能會(huì)與鎖有關(guān)聯(lián)的條件變量。這些變量在用法上與使用 Object.wait 訪問(wèn)的隱式監(jiān)視器類似,但提供了更強(qiáng)大的功能。需要特別指出的是,單個(gè) Lock 可能與多個(gè) Condition 對(duì)象關(guān)聯(lián)。為了避免兼容性問(wèn)題,Condition 方法的名稱與對(duì)應(yīng)的 Object 版本中的不同。

        • 在 Condition 對(duì)象中,與 wait、notify 和 notifyAll 方法對(duì)應(yīng)的分別是await、signal 和 signalAll。

        • Condition 實(shí)例實(shí)質(zhì)上被綁定到一個(gè)鎖上。要為特定 Lock 實(shí)例獲得Condition 實(shí)例,請(qǐng)使用其 newCondition() 方法。

        3. 線程按序交替

        • Lock和Condition結(jié)合應(yīng)用以實(shí)現(xiàn)線程按序交替。


        案例:

        編寫一個(gè)程序,開(kāi)啟 3 個(gè)線程,這三個(gè)線程的 ID 分別為A、B、C,每個(gè)線程將自己的 ID 在屏幕上打印 10 遍,要求輸出的結(jié)果必須按順序顯示。如: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; // 當(dāng)前正在執(zhí)行的線程標(biāo)記
            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 維護(hù)了一對(duì)相關(guān)的鎖,一個(gè)用于只讀操作,另一個(gè)用于寫入操作。只要沒(méi)有 writer,讀取鎖可以由多個(gè) reader 線程同時(shí)保持。寫入鎖是獨(dú)占的。

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

        示例代碼

        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();
          }
         }
        }

        九、線程八鎖

        • 一個(gè)對(duì)象里面如果有多個(gè)synchronized方法,某一個(gè)時(shí)刻內(nèi),只要一個(gè)線程去調(diào)用其中的一個(gè)synchronized方法了,其它的線程都只能等待,換句話說(shuō),某一個(gè)時(shí)刻內(nèi),只能有唯一一個(gè)線程去訪問(wèn)這些synchronized方法。

        • 鎖的是當(dāng)前對(duì)象this,被鎖定后,其它的線程都不能進(jìn)入到當(dāng)前對(duì)象的其它的synchronized方法。

        • 加個(gè)普通方法后發(fā)現(xiàn)和同步鎖無(wú)關(guān)。

        • 換成兩個(gè)對(duì)象后,不是同一把鎖了,情況立刻變化。

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

        總結(jié):

        • 所有的非靜態(tài)同步方法用的都是同一把鎖——實(shí)例對(duì)象本身,也就是說(shuō)如果一個(gè)實(shí)例對(duì)象的非靜態(tài)同步方法獲取鎖后,該實(shí)例對(duì)象的其他非靜態(tài)同步方法必須等待獲取鎖的方法釋放鎖后才能獲取鎖,可是別的實(shí)例對(duì)象的非靜態(tài)同步方法因?yàn)楦搶?shí)例對(duì)象的非靜態(tài)同步方法用的是不同的鎖,所以毋須等待該實(shí)例對(duì)象已獲取鎖的非靜態(tài)同步方法釋放鎖就可以獲取他們自己的鎖。

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

        十、線程池

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

        • 線程池可以解決兩個(gè)不同問(wèn)題:由于減少了每個(gè)任務(wù)調(diào)用的開(kāi)銷,它們通??梢栽趫?zhí)行大量異步任務(wù)時(shí)提供增強(qiáng)的性能,并且還可以提供綁定和管理資源(包括執(zhí)行任務(wù)集時(shí)使用的線程)的方法。每個(gè) ThreadPoolExecutor 還維護(hù)著一些基本的統(tǒng)計(jì)數(shù)據(jù),如完成的任務(wù)數(shù)。

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

        (1)Executors.newCachedThreadPool()(無(wú)界線程池,可以進(jìn)行自動(dòng)線程回收)

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

        (3)Executors.newSingleThreadExecutor()(單個(gè)后臺(tái)線程)

        它們均為大多數(shù)使用場(chǎng)景預(yù)定義了設(shè)置。


        示例代碼

        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;

        /*
         * 一、線程池:提供了一個(gè)線程隊(duì)列,隊(duì)列中保存著所有等待狀態(tài)的線程。避免了創(chuàng)建與銷毀額外開(kāi)銷,提高了響應(yīng)的速度。
         * 
         * 二、線程池的體系結(jié)構(gòu):
         *  java.util.concurrent.Executor : 負(fù)責(zé)線程的使用與調(diào)度的根接口
         *   |--**ExecutorService 子接口: 線程池的主要接口
         *    |--ThreadPoolExecutor 線程池的實(shí)現(xiàn)類
         *    |--ScheduledExecutorService 子接口:負(fù)責(zé)線程的調(diào)度
         *     |--ScheduledThreadPoolExecutor :繼承 ThreadPoolExecutor, 實(shí)現(xiàn) ScheduledExecutorService
         * 
         * 三、工具類 : Executors 
         * ExecutorService newFixedThreadPool() : 創(chuàng)建固定大小的線程池
         * ExecutorService newCachedThreadPool() : 緩存線程池,線程池的數(shù)量不固定,可以根據(jù)需求自動(dòng)的更改數(shù)量。
         * ExecutorService newSingleThreadExecutor() : 創(chuàng)建單個(gè)線程池。線程池中只有一個(gè)線程
         * 
         * ScheduledExecutorService newScheduledThreadPool() : 創(chuàng)建固定大小的線程,可以延遲或定時(shí)的執(zhí)行任務(wù)。
         */
        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. 為線程池中的線程分配任務(wù)
          for (int i = 0; i < 10; i++) {
           pool.submit(tpd);
          }
          
          //3. 關(guān)閉線程池
          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++);
          }
         }
         
        }

        十一、線程調(diào)度

        1. ScheduledExecutorService

        一個(gè) ExecutorService,可安排在給定的延遲后運(yùn)行或定期執(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);//生成隨機(jī)數(shù)
             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 框架:就是在必要的情況下,將一個(gè)大任務(wù),進(jìn)行拆分(fork)成若干個(gè)小任務(wù)(拆到不可再拆時(shí)),再將一個(gè)個(gè)的小任務(wù)運(yùn)算的結(jié)果進(jìn)行 join 匯總。

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

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

                 當(dāng)執(zhí)行新的任務(wù)時(shí)它可以將其拆分分成更小的任務(wù)執(zhí)行,并將小任務(wù)加到線程隊(duì)列中,            然后再?gòu)囊粋€(gè)隨機(jī)線程的隊(duì)列中偷一個(gè)并把它放在自己的隊(duì)列中。

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

        示例代碼

        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("耗費(fèi)時(shí)間為:" + Duration.between(start, end).toMillis());//166-1996-10590
         }
         
         //普通串行計(jì)算
         @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("耗費(fèi)時(shí)間為:" + 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("耗費(fèi)時(shí)間為:" + 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(); //進(jìn)行拆分,同時(shí)壓入線程隊(duì)列
           
           ForkJoinSumCalculate right = new ForkJoinSumCalculate(middle+1, end);
           right.fork(); //
           
           return left.join() + right.join();
          }
         }
        }

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

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

        原文鏈接:

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




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

        ??????

        ??長(zhǎng)按上方微信二維碼 2 秒





        感謝點(diǎn)贊支持下哈 

        瀏覽 52
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            免费无遮挡无码永久在线观看视频 | 日韩免费AV一区 | 好爽免费视频 | 精品韩国一区二区三区 | 黄色小说网站在线观看免费 | 精品一区二区免费播放 | www.狠狠 | 全黄裸片武则天一级 | 久久国内精品99久久6app | www.国产黄片 |