1. 程式人生 > >Java多線程和並發總結

Java多線程和並發總結

避免 rec load 內存模型 es2017 ade book 返回 throws

Java多線程和高並發總結

技術分享

技術分享

    • wait/notify必須存在於synchronized塊中。

    • volatile
      多線程的內存模型:main memory(主存)、working memory(線程棧),在處理數據時,線程會把值從主存load到本地棧,完成操作後再save回去(volatile關鍵詞的作用:每次針對該變量的操作都激發一次load and save)

    • Thread類最佳實踐: 寫的時候最好要設置線程名稱 Thread.name,並設置線程組 ThreadGroup,目的是方便管理。在出現問題的時候,打印線程棧 (jstack -pid) 一眼就可以看出是哪個線程出的問題,這個線程是幹什麽的。

    • 如何獲取線程中異常
      Thread t = new Thread(task);
      t.setUncaughtException(new UncaughtExceptionHandler(){...});
      t.start();
      不能用try,catch來獲取線程中的異常

    • Future和FutureTask
      Future是接口,FutureTask是類

      Future表示一個異步計算的結果. 異步計算是在其他線程進行的, 因此異步計算的結果, 有可能有值, 也有可能沒有值. 於是, Future就提供了一些方法來處理這種未知狀態:
      a. isDone() 異步任務是否完成, 即否有結果

      b. get() 獲取異步任務結果, 如果異步任務未完成, 此方法會一直阻塞, 直到異步方法完成 或 任務被取消 (調用了cancel()方法)
      c. cancel() 取消異步任務, 如果異步任務已經完成, 那麽取消失敗(即cancel()方法返回false)
      d. isCancelled() 查詢異步任務是否已被取消

      FutureTask就是一個可取消的異步任務,FutureTask實現了兩個接口,Runnable和Future,所以它既可以作為Runnable被線程執行,又可以作為Future得到Callable的返回值,那麽這個組合的使用有什麽好處呢?假設有一個很耗時的返回值需要計算,並且這個返回值不是立刻需要的話,那麽就可以使用這個組合,用另一個線程去計算返回值,而當前線程在使用這個返回值之前可以做其它的操作,等到需要這個返回值時,再通過Future得到

      //1
      FutureTask<Integer> future = new FutureTask<Object>(callable);  
      new Thread(future).start();  
      //這裏可以做其他事情  
      future.get();//阻塞獲得結果  
      
      //2
      public void function() {
          // 1. init services
          final ExecutorService executorService = ...
      
          // 2. make task
          FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
              @Override
              public String call() throws Exception {
                  String result = "";
                  //do something;
                  return result;
              }
          });
      
          // 3. submit task
          executorService.submit(futureTask);
      
          // 4. get result
          String result = futureTask.get();
      
          // 5. do something with result
          // ...
      }
      
    • ThreadLocal類
      用處:保存線程的獨立變量。對一個線程類(繼承自Thread) 當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。常用於用戶登錄控制,如記錄session信息。

      實現:每個Thread都持有一個TreadLocalMap類型的變量(該類是一個輕量級的Map,功能與map一樣,區別是桶裏放的是entry而不是entry的鏈表。功能還是一個map。)以本身為key,以目標為value。 主要方法是get()和set(T a),set之後在map裏維護一個threadLocal -> a,get時將a返回。ThreadLocal是一個特殊的容器。

    • 並發包:原子類、鎖類、容器類、管理類總結
      並發包總結1
      並發包總結2

    • cas和aba
      CAS:對於內存中的某一個值V,提供一個舊值A和一個新值B。如果提供的舊值V和A相等就把B寫入V。這個過程是原子性的。
      CAS執行結果要麽成功要麽失敗,對於失敗的情形下一班采用不斷重試。或者放棄。
      ABA:如果另一個線程修改V值假設原來是A,先修改成B,再修改回成A。當前線程的CAS操作無法分辨當前V值是否發生過變化。
      如何解決ABA問題? 通常是使用版本戳來解決這個問題,避免並發中的問題。 CAS是一種樂觀鎖技術

    • 重排序/happens before
      JMM中,如果一個操作執行的結果需要對另一個操作可見,那麽這兩個操作之間必須存在happens-before關系。
      happen-before原則是JMM中非常重要的原則,它是判斷數據是否存在競爭、線程是否安全的主要依據,保證了多線程環境下的可見性。
      happen-before原則解決了重排序帶來的多線程運行問題。
      如果兩個操作之間具有happens-before 關系,那麽前一個操作的結果就會對後面一個操作可見。

      happens-before原則規則:

      1. 程序次序規則:一個線程內,按照代碼順序,書寫在前面的操作先行發生於書寫在後面的操作;
      2. 鎖定規則:一個unLock操作先行發生於後面對同一個鎖額lock操作;
      3. volatile變量規則:對一個變量的寫操作先行發生於後面對這個變量的讀操作;
      4. 傳遞規則:如果操作A先行發生於操作B,而操作B又先行發生於操作C,則可以得出操作A先行發生於操作C;
      5. 線程啟動規則:Thread對象的start()方法先行發生於此線程的每個一個動作;
      6. 線程中斷規則:對線程interrupt()方法的調用先行發生於被中斷線程的代碼檢測到中斷事件的發生;
      7. 線程終結規則:線程中所有的操作都先行發生於線程的終止檢測,我們可以通過Thread.join()方法結束、Thread.isAlive()的返回值手段檢測到線程已經終止執行;
      8. 對象終結規則:一個對象的初始化完成先行發生於他的finalize()方法的開始;
    • 線程池
      Java通過Executors提供四種線程池,分別為:
      (緩存、固定、定時、單例)

      1. newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
      2. newFixedThreadPool 創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。
      3. newScheduledThreadPool 創建一個定長線程池,支持定時及周期性任務執行。(定時任務建議用這個,不要用TimerTask,因為:異常退出所有任務)
      4. newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
    • 參考文獻
      文獻1-並發多線程
      文獻2-並發編程指南
      文獻3-AQS
      文獻4-concurrentHashMap

Java多線程和並發總結