effect java 學習摘要(8) - 併發

同步訪問共享的可變數據

  • synchronized : 可以保證在同一時刻,只有一個線程可以執行某一個方法, 或者某一個代碼塊.

  • 多線程訪問, 可能會導致同一對象狀態發生變化. 同步 可以使多線程看到由同一個鎖保護的之前所有的修改效果.

  • 非long或double類型的變量, 讀寫操作一個變量是原子的

  • 在線程之間進行可靠的通信, 也爲了互斥訪問, 同步是必要的.

  • 不要使用 Thread.stop() , 要阻止一個線程妨礙另一個線程 ,正確做法如下 :

    public class StopThread {
    
        public static boolean stopRequested;
    
        private static synchronized void requestStop() {
            stopRequested = true;
        }
    
        public static synchronized boolean stopRequested() {
            return stopRequested;
        }
    
        public void stopThread() throws InterruptedException {
            Thread backgroundThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    int i = 0;
                    while (!stopRequested()) {
                        i++;
                    }
                }
            });
            backgroundThread.start();
            TimeUnit.SECONDS.sleep(1);
            requestStop();
        }
    }
    
  • 如果讀寫操作都沒有進行同步 , 那麼同步就不會起作用.

    public class StopThread {
    
        public static volatile boolean stopRequested;
    
        public void stopThread() throws InterruptedException {
            Thread backgroundThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    int i = 0;
                    while (!stopRequested) {
                        i++;
                    }
                }
            });
            backgroundThread.start();
            TimeUnit.SECONDS.sleep(1);
            stopRequested = true;
        }
    }
    
  • 多個線程共享可變數據的時候, 每個讀或者寫數據的線程都必須執行同步

避免過度同步

  • 共享數據類型,需要使用線程安全的數據結構 , 例如 : CopyOnWriteArrayList

executor和task優先於線程

  • 儘量避免自己編寫工作隊列, 儘量不要直接使用線程, 使用java提供的executor線程池

併發工具優先於wait和notify

  • 線程高級工具 :

    • Executor Framework
    • 併發集合
    • 同步器 : CountDownLatch 和 Semaphore
      public class Synchronizer {
      
          final Executor executor = Executors.newSingleThreadExecutor();
          final CountDownLatch ready = new CountDownLatch(3);
          final CountDownLatch start = new CountDownLatch(1);
          final CountDownLatch done = new CountDownLatch(3);
          final Runnable runnable = new Runnable() {
              @Override
              public void run() {
                  //do nothing
              }
          };
      
          private long test() throws InterruptedException {
              for (int i = 0;i < 3; i++) {
                  executor.execute(new Runnable() {
                      @Override
                      public void run() {
                          ready.countDown();
                          try {
                              start.await();
                              runnable.run();
                          } catch (InterruptedException e) {
                              Thread.currentThread().interrupt();
                          } finally {
                              done.countDown();
                          }
      
                      }
                  });
              }
              ready.await();
              long startNanos = System.nanoTime();
              start.countDown();
              done.await();
              return System.nanoTime() - startNanos;
          }
      }
      
  • 調用wait方法,應當用循環之內調用 , 永遠不要在循環外調用
  • 優先使用notifyAll , 當只有一個線程被喚醒才使用notify

延遲初始化

  • lazy initializition holder class 模式 : 常見用於單例模式

        private static class FieldHolder {
            static final FieldType field = computeFieldValue();
        }
    
        static FieldType getFieldType() {
            return FieldHolder.field;
        }
    

避免使用線程組

  • ThreadGroup 已經爲過時的API, 沒必要繼續使用
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章