日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

读Java并发编程实践记录_原子性_锁_同步容器详解_任务执行

發布時間:2024/7/5 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 读Java并发编程实践记录_原子性_锁_同步容器详解_任务执行 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原子性: 單獨的,不可分割的操作

  • 不要使用過期狀態值來決策當下的狀態, 一定要先檢查再執行(不檢查, 將引發數據修改,丟失)
  • 避免延遲初始化(懶加載: 先查看對象 == null, 然后new), 有可能查看對象狀態的時候, 對象已經new出來, 只不過還沒將對象賦給引用
  • 避免復合操作, 例: count++; 非原子性操作, 包含讀-改-寫三個過程, 不加鎖, 必然出錯; 強行加鎖, 使得整個復合操作變為原子性
  • 使用atomic包下類(線程安全類), 讀寫操作都是加鎖狀態
  • 修飾代碼塊:大括號括起來,作用于對象
  • 修飾方法:方法名前使用, 作用于對象
    在方法內部使用synchronized代碼塊,與修飾方法時效果是一致的
  • 修飾靜態方法:整個靜態方法,作用于所有對象
  • 修飾類:類名前添加,作用于所有對象
    注:
    1. 作用于對象時,不同調用對象之間不影響
    2. 使用synchronized的父類(synchronized不屬于方法聲明的一部分),子類繼承后需要對其方法重新添加synchronized修飾,不然不能使用同步
  • 特點:
    synchronized:不可中斷鎖,適用于競爭不激烈,可讀性好
    Lock:可中斷鎖,多樣化同步,競爭激烈時能維持常態
    Atomic:競爭激烈時能維持常態,比Lock性能好

  • 在線程中使用atomic包下類, 不一定是線程安全的, 例:
  • AtomicInteger i1 = new AtomicInteger(1);AtomicInteger i2 = new AtomicInteger(1);if(i1.incrementAndGet() > 1){//輸出簡寫sout(i2.get());} 看似i1, i2各自是線程安全的, 但是他們合起來的操作就不再是原子性的, 多個線程操作必然出錯.
  • synchronized可以鎖住對象引用, 也可鎖對象本身, 從Thread, Runnable實現線程就可知
  • /*** @author regotto* 測試線程鎖住公共資源*/public class Demo2 {public static void main(String[] args) {StringBuffer sb = new StringBuffer();new People("張三", sb).start();new People("李四", sb).start();}}class People extends Thread{private StringBuffer sb;public People(){}public People(String name, StringBuffer sb){this.setName(name);this.sb = sb;}@Overridepublic void run() {final int c = 10;/*這里的線程都是在extends Thread的基礎上這里不能使用synchronized(this)(在函數名前加synchronized效果一樣),由于前面新建兩個線程,對應的this都是不同的,獲取的不是同一把鎖,當使用同一個Thread就可使用synchronized(this)當使用synchronized(sb)就是正確的,此時鎖住的是sb,兩個線程都拿著同一個sb,所以可以正確鎖住,*/// synchronized (this){// for (int i = 0; i < c; i++) {// sb.append(this.getName()).append(" ");// System.out.println("我是:"+this.getName()+" sb:"+sb);// try {// Thread.sleep(200);// } catch (InterruptedException e) {// e.printStackTrace();// }// }// }synchronized(sb){for (int i = 0; i < c; i++) {sb.append(this.getName()).append(" ");System.out.println("我是:"+this.getName()+" sb:"+sb);try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}}}}}
  • 重入鎖(ReentrancyLock): 當前線程可以多次獲得同一把鎖, 例如:lock();lock();此時線程獲得2把鎖(線程內部具有鎖計數器)

  • long/double在JVM中會被拆分為兩個32位讀取, 多線程下, 將會導致讀取到一個憑空而來的值(而不是過期或者正確的數值)

  • volatile不具有原子性, 他不能使count++具有原子性, 他只具有可見性, 線程中共享變量使用它

  • 使用volatile的標準:
    1. 確保只有單一的線程修改變量的值
    2. 變量不需要與其他狀態變量共同參與不變約束
    3. 訪問變量, 沒有其他原因要加鎖

  • ThreadLocal, 線程本地變量, JDBC中使用ThreadLocal存儲Connection, 使得每個線程都能從連接池中獲得自己的Connection

  • 嘗試將單線程中的內容遷移到多線程中, 嘗試將共享變量轉化到ThreadLocal中

  • 不可變對象天生就就是線程安全的, 其狀態永遠都無改變

  • 使用不可變對象, 保證操作的原子性, 在1中說到兩個原子類的和操作并不一定是原子操作(他們各自的操作是原子操作), 這里我可以提供一個不可變容器, 例如:
    final AtomicInteger i3;
    final AtomicInteger i4;
    //不可變容器
    public init(AtomicInteger i1, AtomicInteger i2){
    final AtomicInteger i3 = i1; final AtomicInteger i4 = i2;
    }
    //執行此操作的時候, 多線程情況下不會出現i3或i4為過期值,解決了原子操作
    public AtomicInteger sum(){ return i3.get()+i4.get();}
    注: 每次執行都開辟內存, 但是相比于synchronized, 這點性能損耗不算什么.

  • 使用線程安全的容器, 例如Hashtable, synchronizedMap, ConcurrentMap, Vector, BlockingQueue, ConcurrentLinkedQueue存儲對象, 多線程下保證了從容器中取出的對象總是線程安全

  • 不可變對象可以通過任意機制發布, 線程安全容器中的對象必須安全發布, 可變對象必須要安全發布,并且使用鎖保護

  • 共享對象線程安全的有效策略:
    1. 線程限制, ThreadLocal處理
    2. 共享只讀, 使用不可變對象/使用線程安全容器存儲該對象
    3. 共享線程安全, 在對象內部同步, 使用公共接口進行訪問
    4. 被守護對象, 只能通過特定的鎖進行訪問

  • 設計線程安全的類:
    1. 確定對象狀態由哪些變量組成
    2. 限制狀態的不變約束
    3. 制定一個并發管理策略–使用12中策略

  • 任意所對象都可以用來保護對象狀態:
    1. 使用私有對象作為鎖對象, 而不是對象內部鎖;
    2. 線程使用的對象可以是克隆對象, 避免線程對真正的對象產生修改

  • 解決1中的問題的時候, 由于兩個變量是相互獨立, 且之間存在一定的聯系, 所以加鎖的時候可以使用公共鎖進行處理使用一個公共變量作為鎖對象, 每當需要處理其中一個變量的時候, 都是通過公共鎖防止其他線程修改另一變量, 導致兩個變量之間的關系出現變化
    注: 面對這樣的問題的時候, 使用volatile已經沒有用, volatile不能處理這樣的復合操作, 使得整個操作具有原子性

  • 狀態變量是否線程安全取決于當前發布的狀態(暴露給用戶的狀態,可以對公共接口進行加鎖, 對當前變量設置final, 可以使用volatile, 可以使用線程安全的容器)

  • "先檢查再運行"將導致嚴重的線程安全問題, 例如延遲加載; 在add操作的時候,對象還沒添加到容器中, 另一線程檢查將會發現也可以進行add, 這樣將會有多次add, 導致同一個容器存在多份相同的對象

  • 向一個類中添加一個原子操作, 最有效的選擇是組合

  • 同步容器:

  • 同步容器都是線程安全的, 但是針對同步容器的復合操作不是原子性的, 需要對操作之間存在關系的那個對象/變量加鎖, 使得復合操作是原子性的
    例如: 多個線程對Vector執行remove, get將有可能引發數組越界的問題/訪問元素不存在, 這時候需要重新對list加鎖, 保證只有一個線程對公共資源進行操作
  • 迭代器/forEach遍歷需要加鎖, 在遍歷過程中, 另一線程很有可能remove某些元素, 導致遍歷過程出現修改異常(ConcurrentModificationException)但是迭代過程中, 如果對容器加鎖, 將會導致性能極大下降, 還有可能出現死鎖, 饑餓風險, 采用的解決方案是復制容器, 這樣即使容器被修改了, 但是復制品也不會被修改, 避免遍歷過程出現的修改異常, 但是復制也將導致性能損耗;還有一個問題就是, 在程序中很有可能出現隱藏的迭代器, 這也將引起非常隱蔽的線程安全問題
  • 例: for(){sout("AAAAAAB"+i); //這里的字符串連接隱含這是使用迭代器遍歷,多線程操作很容易引起修改異常}
  • ConcurrentHashMap使得不再是只有一個線程能同時訪問容器; 內部使用分離鎖, 使得可以多個線程執行讀, 寫操作且不影響性能; 由于并發操作, 使用size, isEmpty被弱化;
    synchronizedMap卻是為Map中每個方法同步操作而言, 并發情況下具有性能上的損耗
  • CopyOnWriteArrayList避免了迭代期間對容器的加鎖與復制, 底層存在的是一個不可變的基礎數組引用, 對數組的修改總是用復制品去操作, 避免線程讀的時候出現修改異常,但是修改頻率較高的時候就會出現性能上的下降.
  • 阻塞隊列–生產-消費者模式, 有效避免了負載, 例如: 線程池, BlockingQueue維護的是一個線程隊列, 他不維護隊列元素儲存空間
  • 雙端隊列–竊取工作, 當自己隊列中任務已經完成, 就竊取其他隊列末尾的工作(避免競爭), 相比于傳統消費者工作模式, 雙端隊列具有更強的伸縮性(生產者生產的內容很多, 竊取工作很容易加快消費者的消費行為, 使得每個線程都能高效運行)
  • 阻塞可中斷: 可阻塞方法使用interrupt提前中斷, Runnable的interrupt必須進行捕獲, extends Thread的可以拋給調用者
  • Synchronizer: 一個對象, 根據本身狀態調節線程控制流; 例如阻塞隊列, Semaphore等
  • 閉鎖: 延遲線程的進度直到, 直到終點狀態到達, 才允許所有線程往下執行,
    例如:
  • 資源R已經被初始化, 但R執行的活動都在閉鎖中等待
  • 確保一個服務不會開始, 直到他所依賴的所有服務都已開始
  • 等待所有活動都做好準備, 然后接著往下執行
  • CountDownLatch:允許一個或多個線程等待一個事件集的發生, 內部使用countDown計數, 當由線程到達, 執行計數且進行await,當計數器達到某一狀態, 就notifyAll
  • FutureTask ==> 有返回值的Runnable, 使用Future.get獲得返回值, 當線程還在運行中還沒返回值, 此時get將會阻塞, 直到線程具有返回值, 阻塞到一定狀態將會拋出異常
  • Executor利用FutureTask完成異步操作
  • ThreadPoolExecutor:
    1. corePoolSize: 核心線程數, 運行的線程數小于corePoolSize時,直接創建新的線程,即使存在空閑線程
    2. maximumPoolSize: 線程最大線程數
    3. workQueue: 阻塞隊列,存儲等待執行的任務,會對線程池運行過程產生重大的影響
    4. 只有當線程數大于maximumPoolSzie但workQueue還沒滿,則將線程放入workQueue中, 若workQueue滿了, 則選擇合適的策略執行后續操作
    5. keepAliveTime: 當其沒有任務執行,除核心線程,當超出指定時間,這些線程就銷毀
    6. unit: keepAliveTime的時間單位
    7. threadFactory: 線程工程, 用于創建線程
    8. rejectHandler: workQueue中的策略處理
    具有如下策略: 1.拋出異常, 2.用調用者所在的線程執行任務(默認), 3.丟棄阻塞隊列中最靠前的任務,執行當前任務,4.直接丟棄當前任務
  • Executor異步執行任務, 不能正確關閉將會阻止JVM結束
  • 線程池狀態:
  • running: 可以處理新提交的任務以及阻塞隊列中的任務
  • shutdown: 調用shutdown()進入,只能處理新提交的任務, 但是可以處理workQueue中的剩余任務, 阻塞隊列為空, 線程池中線程數為0, 進入tidying
  • stop:調用shutdownNow(),不能處理任何任務(任務隊列中任務全部取消),線程池中線程數為0, 進入tidying
  • tidying: 線程池處理后續任務
  • 常用方法
  • execute(): 提交任務, 交給線程池執行
  • submit(): 提交任務, 能返回執行結果, execute+Future
  • shutdown(): 關閉縣此次, 等待任務執行完
  • shutdownNow(): 關閉線程池, 不等待任務執行完
  • getTaskCount(): 線程池已執行和未執行的任務總數
  • getCompletedTaskCount(): 已完成的任務總數
  • getPoolSize(): 線程池當前的線程數量
  • getActiveCount(): 當前線程池中正在執行的任務數量
  • newFixedThreadPool: 創建定長的線程池, 每當提交一個任務就創建一個線程, 直到達到最大長度(長度就不再變化)
  • newCachedThreadPool: 創建的線程超出Max,則靈活收回空閑線程,需要增加時, 靈活添加, 對池的大小不做限制
  • newSingleThreadExecutor: 創建單線程化executor, 當該線程異常結束, 會立即創建新線程取代, 從而保證任務隊列中任務按照規定順序執行(FIFO, LIFO, 優先級)
  • newScheduledThreadPool: 創建定長線程池, 可定時周期性任務執行
  • 同步容器詳解

  • AQS
  • 使用Node實現FIFO隊列, 用于構建鎖或其他同步裝置的基礎框架
  • 利用int類型表示狀態, 表示當前鎖是哪種類型(輕量級, 重量級, 重入鎖, 偏向鎖…)
  • 使用方法是繼承,內部使用模板方法
  • 子類通過繼承并通過使用實現它的方法管理其狀態(acquire, release方法操作狀態)
  • 可以同時實現排它鎖和共享鎖模式(獨占, 共享)
  • AQS同步組件:
  • CountDownLatch: 通過計數判斷線程是否需要阻塞
  • Semaphore: 控制同一時間的并發數目
  • CyclicBarrier: 與CountDownLatch相似
  • ReentrantLock
  • Condition
  • FutureTask
  • CountDownLatch:
    線程調用await將會阻塞, 其他線程調用countDown()會使計數器減1, 當計數器值為0時, 因調用await()而等待的線程將由await變為喚醒
    此種情況只會出現一次, 計數器不能被重置, 業務上需要重置, 就使用CyclicBarrier
  • 例:public static void main(String[] args){Executor exec = new Executors.newCachedThreadPool();final CountDownLatch cdl = new CountDownLatch(200);for(int i = 0; i < 200; i++){final int threadNum = i;exec.execute(()->{try{test(threadNum);}catch(Exception e){e.printStack();}finally{cdl.countDown();//一個線程執行后計數器減1}});}cdl.await();//只有當計數器為0才喚醒所有線程, 否則線程執行此行代碼就線程阻塞sysout("finish");exec.shutdown();//等線程池中線程執行完,就關閉線程池}public void static test(int t){Thread.sleep();sysout(t);}

    運用場景: 在指定時間內完成指定任務, cdl.await(10, TimeUtit.MILLISECONDS)//10代表大小, 后面代表單位,10毫秒,等待的時間是指從線程開始執行到await的時間.

  • Semaphore: 提供對有限資源的訪問次數, 類似于生產消費者模式
  • 例:public static void main(String[] args){Executor exec = new Executors.newCachedThreadPool();final Semaphore sp = new Semaphore(3);//執行一次放出3個許可for(int i = 0; i < 200; i++){final int threadNum = i;exec.execute(()->{try{sp.acquire();//獲取許可,默認獲取一個,執行一次放出3個許可, 此處一次獲取1個即表示一次可以同時執行3個線程//如果寫成sp.acquire(3)表示一次需要獲取3個許可,而構造只給出3個, 因此類似單線程執行, 每次等待3個線程,然后3個都執行完才進行releasetest(threadNum);sp.release();//釋放許可,與獲取許可一直, 可以單個逐一釋放, 也可以一次性釋放多個}catch(Exception e){e.printStack();}});}exec.shutdown();//等線程池中線程執行完,就關閉線程池}public void static test(int t){Thread.sleep();sysout(t);}///if(sp.tryAcquire()){test(threadNum);sp.release();}//表示嘗試獲取許可,如上述代碼, 嘗試獲取3個許可, 當時這三個線程還未執行完, 其他線程無法獲取許可, 就不能執行if中的內容, 相當于丟棄其他線程, 可傳入參數設定嘗試許可數以及等待時間

    注: Semaphore并沒有真正向線程分配許可, 一個線程得到的許可可能是由另外的一個線程釋放, 使用Semaphore實現資源池, 有資源的時候acquire, 資源使用結束后release放回資源池, 一個創建資源池最簡單的方式就是使用BlockingQueue

  • CyclicBarrier:一組線程同時等待, 只有當所有的線程都到達屏障點, 所有線程才能繼續往下執行后續代碼
  • 例:private static CyclicBarrier cb = new CyclicBarrier(5);//一次5個線程為一組, 5個執行完再執行后續線程//在聲明CyclicBarrier的時候可以指定Runnable//表示線程到達await的時候優先執行Runnable中的內容//例: new CyclicBarrier(5, ()->{sysout("----------");//每一組線程都到await時,先執行Runnable,然后再執行后續代碼});public static void main(String[] args){Executor exec = new Executors.newCachedThreadPool();for(int i = 0; i < 200; i++){final int threadNum = i;Thread.sleep(1000);exec.execute(()->{try{test(threadNum);}catch(Exception e){e.printStack();}});}exec.shutdown();//等線程池中線程執行完,就關閉線程池}public void static test(int t) throws Exception{Thread.sleep(1000);try{barrier.await(2000, TimeUnit.MILLISECONDS);//在規定時間等待線程, 超出2秒后就拋出異常} catch(BrokenBarrierException | TimeoutException e) {sysout(e);//不捕獲異常就會導致后續代碼不執行}sysout(t);}

    當線程到達屏障, 調用await, 直到所有的線程都到達, 進行notifyAll
    注: 可用做并行迭代算法, 問題分解

  • ReentrantLock(可重入鎖):屬于自旋鎖, 死循環調用CAS實現鎖, 而非等待數據進入內核態加鎖
  • ReentrantLock與synchronized的區別:
    可重入性,二者區別不大,使用所計數器,計算當前鎖的數目
    鎖的實現, ReentrantLock依賴于API, synchronized依賴于JVM
    性能, 當synchronized引入輕量級,偏向,重量級鎖后, 二者性能差不多
  • ReentrantLock獨有功能
    可指定公平鎖還是非公平鎖
    提供一個Condition類, 可以分組喚醒需要喚醒的線程, 而synchronized的單個喚醒是隨機的
    提供中斷等待鎖的線程機制, lock.lockInterruptibly()實現
  • 例:private final static Lock lock = new ReentrantLock();public static void main(String[] args){ExecutorService es = new Executors.newCachedThreadPool();final CountDownLatch cd = new CountDownLatch(2000);final Semaphore sp = new Semaphore(200);for(int i = 0 ;i < 2000; i++){es.execute(()->{try{sp.acquire();add();sp.release();}catch(Exception e){sysout(e);}});}cd.await();es.shutdown();sysout("finish"+count);//看執行結果是否為2000}private static void add(){lock.lock();try{count++;}finally{lock.unclock();}}
  • ReentrantReadWriteLock://使用場景不多
    內部兩個核心成員: ReentrantReadWriteLock.ReadLock readLock; ReentrantReadWriteLock.WriteLock writeLock;
    在讀寫未被打擾的情況下才能執行讀寫鎖的操作
  • 例:ReentrantReadWriteLock lock = new ReentrantReadWriteLock();Lock readLock = lock.readLock();Lock writeLock = lock.writeLock();針對讀寫操作分別使用readLock和writeLock加鎖實現的悲觀操作, 當獲取writeLock的時候不允許有讀操作, 當讀的操作頻率大于寫的操作時就會造成線程饑餓(寫操作總會因為讀操作而中斷)
  • StampedLock://在讀操作較多的場景下具有優勢
    使用悲觀鎖(強行認為讀的時候不能有寫操作)和樂觀鎖(認為讀寫發生的沖突很小)操作
    先使用樂觀策略, 當出現錯誤的時候轉為悲觀鎖
    StampedLock sl = new StampedLock();
    long stm = sl.writeLock();//加鎖會有long型返回值
    sl.unclock(stm);//釋放鎖傳入stm參數

  • Condition://用的很少
    ReentrantLock rl = new ReentrantLock();
    Condition c = rl.newCondition();
    在run()中寫如下
    rl.clock();//線程加鎖
    c.awatir();//當前線程從AQS隊列中移除, 相當于鎖的釋放, 將該線程加入到AQS中的單向隊列中, 等待信號
    c.signalAll()//喚醒所有的等待線程, 也可以單個喚醒
    注: 一定要記住有unclock的存在

  • FutureTask//很重要, 融合了Thread, Runnable, Future, Callable
    Callable(有返回值,可拋出異常), Runnable
    Future(監視目標線程調用call()情況, 獲取返回值, 若call未返回值, 則Future線程阻塞)
    FutureTask實現Runnable, Future, 可以操作Runnable對象也可以獲得線程返回值

  • 例:FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>(){@Overridepublic String call() throws Exception {Thread.sleep(5000);return "Done";}});new Thread(futureTask).start();Thread.sleep(1000);String result = futureTask.get();//獲取返回值FutureTask的構造方法:FutureTask(Runnable r, V v);v表示返回值類型, 此時的Runnable也可具有返回值; 是將Runnable轉化為Callable接口, 然后再調用call方法
  • Fork/Join
    將一個大的任務拆分多個小任務, 每個小任務放置到不同的雙端隊列中, 每個線程執行自己隊列里面的值, 當自己隊列中任務執行完就去其他隊列中偷取任務執行, 每次任務的偷取是從隊列的首開始, 原本執行該任務的線程取任務是從隊列的末端開始
    局限性:
    任務只能使用Fork/Join的同步操作, 不能使用其他的同步操作
    任務不是拋出檢查異常, 不能執行IO操作

  • BlockingQueue
    對滿的隊列進行put(o),將阻塞; add(o),將拋出異常; offer(o)//將返回false,反之true; offer(o, timeOut, timeunit)//操作在指定時間未執行, 就返回false
    對空的隊列進行take(),將阻塞; remove(o)//拋出異常; poll()//返回false,同上; poll(timeout,timenuit)//同上

  • 生產-消費者模式
    ArrayBlockingQueue//容量有限, 初始化后不能改變, 先進先出
    DelayQueue//內部有序,按照元素過期時間排序
    LinkedBlockingQueue
    PriorityBlockingQueue//允許插入null, 插入對象按照Comparable接口的方式排序, 使用迭代器獲取元素的時候并不一定按照優先級獲取
    SynchronousQueue//內部僅容納一個元素

  • 任務執行

  • 在線程中執行任務:
  • 為任務創建線程, 例:socket,如果只是用單線程處理每個用戶的請求,那么性能將會是糟糕的.final ServerSocket socket = new ServerSocket(8080);while(true){Socket connection = socket.accept();new Thread(()->{execute(connection);}).start;}
  • 根據Socket得出如下結論:
  • 讓執行任務脫離主線程,防止因執行任務出現異常導致主線程error; 讓主線程在完成前面的請求之前接收新的請求, 提高響應速度
  • 并行處理任務, 使得多個請求可以同時得到服務
  • 執行的任務必須保證線程安全
  • Executor 線程池(生產者-消費者模式)
    //創建100大小的線程池
    Executor exec = Executor.newFixedThreadPool(100)
  • 創建周期性,延遲性任務
    Timer存在缺陷, 他只采用唯一線程執行,導致多個任務執行的時候影響其他任務的準確性并且他還沒有處理檢查異常,線程執行的過程中將出現無法預料的錯誤, 應考慮ScheduleThreadPoolExecutor
  • 尋找可強化的并行性
    1. 順序執行頁面渲染, 將導致渲染圖片是cpu資源的浪費,如果將問題分散到獨立并發執行中, 將會獲得更好的cpu性能: 使用一個線程渲染文本, 一個線程渲染圖片, 通過Future.get判斷當前線程執行的程度
    2. 可攜帶結果的任務: Callable, Future, Executor使用Runnable, 但是Runnable具有局限性(不能返回值或拋出檢查異常), 可以使用PrivilegedAction將任務類型封裝成Callable
    3. 并行的局限性, 分配任務的時候, 任務協調上的開銷, 不能多于并行性帶給生產力的提高; 例如:1中頁面渲染,若渲染文字的速度遠遠大于渲染圖片,那么使用多線程將會導致程序復制到增加,代碼更加冗余繁雜;
    注: 大量相互獨立且同類的任務進行并發處理, 將不同的任務量分配到不同的任務中才能獲得性能上的提升
    4. CompletionService: 執行批量操作的線程池, 當使用Executor執行批量操作的時候帶來的問題就是:每獲得一個結果都需要使用Future.get(),此過程還會出現error,阻塞等問題;
    5. CompletionService執行批量操作(一般使用子類ExecutorCompletionService, 它在構造函數中創建BlockingQueue儲存所有任務),將所有任務結果封裝到一個QueueingFuture(FutureTask子類)中, 使用隊列操作從中獲得線程返回的結果;
    多個ExecutorCompletionService可以共享單一的Executor
    6. 為任務設置時限, 避免銷毀過多的資源, 使用Future.get(get在計算時間的時候是使用當前時間減去預測時間, 任務最開始執行的時候得到的結果可能是負數, 在concurrent包下,所有時間負數都按0處理,所以不需要擔心),當拋出TimeoutException,就可以直接Future.cancel,取消當前任務
  • try{//在timeLeft的時間內等待,以NANOSECONDS為單位x = f.get(timeLeft, NANOSECONDS);}catch(Exception e){x = default;//超出時間,拋異常,取消當前任務f.cancel(true);}

    未完待續…

    上面有錯, 還請指出, 如果認為我寫的還不錯, 還請點個贊, 多多支持一下, O(∩_∩)O~~

    總結

    以上是生活随笔為你收集整理的读Java并发编程实践记录_原子性_锁_同步容器详解_任务执行的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。