国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

貓頭鷹的深夜翻譯:核心JAVA并發(一)

Richard_Gao / 550人閱讀

摘要:簡介從創建以來,就支持核心的并發概念如線程和鎖。這篇文章會幫助從事多線程編程的開發人員理解核心的并發概念以及如何使用它們。請求操作系統互斥,并讓操作系統調度程序處理線程停放和喚醒。

簡介

從創建以來,JAVA就支持核心的并發概念如線程和鎖。這篇文章會幫助從事多線程編程的JAVA開發人員理解核心的并發概念以及如何使用它們。

(博主將在其中加上自己的理解以及自己想出的例子作為補充)

概念
原子性:原子操作是指該系列操作要么全部執行,要么全部不執行,因此不存在部分執行的狀態。
可見性:一個線程能夠看見另一個線程所帶來的改變。
競爭情況

當多個線程在一個共享的資源上執行一組操作時,會產生競爭。根據各個線程執行操作的順序可能產生多個不同結果。下面的代碼不是線程安全的,value可能會被初始化多次,因為check-then-act型(先判斷是否為null,然后初始化)的惰性初始化并非原子性操作

class Lazy  {
  private volatile T value;
  T get() {
    if (value == null)
      value = initialize();
    return value;
  }
}
數據沖突

當兩個或多個線程在沒有同步的情況下試圖訪問同一個非final變量時,會產生數據沖突。不使用同步可能使數據的改變對別的線程不可見,從而可能讀取過期的數據,并導致如無限循環,數據結構損壞和不準確的計算等后果。下面這段代碼可能會導致無限循環,因為讀者線程可能永遠都沒有看到寫入者線程做出的更改:

class Waiter implements Runnable {
  private boolean shouldFinish;
  void finish() { shouldFinish = true; }
  public void run() {
    long iteration = 0;
    while (!shouldFinish) {
      iteration++;
    }
    System.out.println("Finished after: " + iteration);
  }
}
class DataRace {
  public static void main(String[] args) throws InterruptedException {
    Waiter waiter = new Waiter();
    Thread waiterThread = new Thread(waiter);
    waiterThread.start();
    waiter.finish();
    waiterThread.join();
  }
}
JAVA內存模型:happens-before關系

JAVA內存模型是根據讀寫字段等操作來定義的,并在控制器上進行同步。操作根據happens-before關聯排序,這解釋了一個線程何時能夠看到另一個線程操作的結果,以及是什么構成了一個同步良好的程序。

happens-before關聯有以下屬性:

Thread#start的方法在線程的所有操作之前執行

在釋放當前控制器之后,后序的請求才可以獲取控制器。(Releasing a monitor happens before any subsequent acquisition of the same monitor.)

寫入volatile變量的操作在所有后序讀取該變量的操作之前執行。

寫入final型變量的操作在發布該對象的引用之前執行

線程的所有操作在從Thread#join方法返回之前執行

上圖中,Action XAction Y之前執行,因此線程1Action X以前執行的所有操作對線程2Action Y之后的所有操作可見。

標注的同步功能 synchronized關鍵字

synchronized關鍵字用來防止不同的線程同時進入一段代碼。它確保了你的操作的原子性,因為你只有獲得了這段代碼的鎖才能進入這段代碼,使得該鎖所保護的數據可以在獨占模式下操作。除此以外,它還確保了別的線程在獲得了同樣的鎖之后,能夠觀察到之前線程的操作。

class AtomicOperation {
  private int counter0;
  private int counter1;
  void increment() {
    synchronized (this) {
      counter0++;
      counter1++;
    }
  }
}

synchronized關鍵字也可以在方法層上聲明。

靜態方法:將持有該方法的類作為加鎖對象
非靜態方法:加鎖this指針

鎖是可重入的。所以如果一個線程已經持有了該鎖,它可以一直訪問該鎖下的任何內容:

class Reentrantcy {
  synchronized void doAll() {
    doFirst();
    doSecond();
  }
  synchronized void doFirst() {
    System.out.println("First operation is successful.");
  }
  synchronized void doSecond() {
    System.out.println("Second operation is successful.");
  }
}

爭用程度影響如何獲得控制器:

初始化:剛剛創建,沒有被獲取
biased:鎖下的代碼只被一個線程執行,不會產生沖突
thin:控制器被幾個線程無沖突的獲取。使用CAS(compare and swap)來管理這個鎖
fat:產生沖突。JVM請求操作系統互斥,并讓操作系統調度程序處理線程停放和喚醒。
wait/notify

wait/notify/notifyAll方法在Object類中聲明。wait方法用來將線程狀態改變為WAITING或是TIMED_WAITING(如果傳入了超時時間值)。要想喚醒一個線程,下列的操作都可以實現:

另一個線程調用notify方法,喚醒在控制器上等待的任意的一個線程

另一個線程調用notifyAll方法,喚醒在該控制器上等待的所有線程

Thread#interrupt方法被調用,在這種情況下,會拋出InterruptedException

最常用的一個模式是一個條件性循環:

class ConditionLoop {
  private boolean condition;
  synchronized void waitForCondition() throws InterruptedException {
    while (!condition) {
      wait();
    }
  }
  synchronized void satisfyCondition() {
    condition = true;
    notifyAll();
  }
}

記住,要想使用對象上的wait/notify/notifyAll方法,你首先需要獲取對象的鎖

總是在一個條件性循環中等待,從而解決如果另一個線程在wait開始之前滿足條件并且調用了notifyAll而導致的順序問題。而且它還防止線程由于偽喚起繼續執行。

時刻確保你在調用notify/notifyAll之前已經滿足了等待條件。如果不這樣的話,將只會發出一個喚醒通知,但是在該等待條件上的線程永遠無法跳出其等待循環。

博主備注:這里解釋一下為何建議將wait放在條件性循環中、假設現在有一個線程,并沒有將wait放入條件性循環中,代碼如下:

class UnconditionLoop{
    private boolean condition;
    
    synchronized void waitForCondition() throws InterruptedException{
        //....
        wait();
    }
    
    synchronized void satisfyCondition(){
        condition = true;
        notifyAll();
    }
}

假設現在有兩個線程分別同時調用waitForConditionsatisfyCondition(),而調用satisfyCondition的方法先調用完成,并且發出了notifyAll通知。鑒于waitForCondition方法根本沒有進入wait方法,因此它就錯過了這個解掛信號,從而永遠無法被喚醒。

這時你可能會想,那就使用if判斷一下條件唄,如果條件還沒滿足,就進入掛起狀態,一旦接收到信號,就可以直接執行后序程序。代碼如下:

class UnconditionLoop{
    private boolean condition;
    
    private boolean condition2;
    
    synchronized void waitForCondition() throws InterruptedException{
        //....
        if(!condition){
            wait();
        }
    }
    synchronized void waitForCondition2() throws InterruptedException{
        //....
        if(!condition2){
            wait();
        }
    }
    synchronized void satisfyCondition(){
        condition = true;
        notifyAll();
    }
    
    synchronized void satisfyCondition2(){
        condition2 = true;
        notifyAll();
    }
}

那讓我們再假設這個 方法中還存在另一個condition,并且也有其對應的等待和喚醒方法。假設這時satisfyConsition2被滿足并發出nofityAll喚醒所有等待的線程,那么waitForConditionwaitForCondition2都將會被喚醒繼續執行。而waitForCondition的條件并沒有被滿足!

因此在條件中循環等待信號是有必要的。

volatile關鍵字

volatile關鍵字解決了可見性問題,并且使值的更改原子化,因為這里存在一個happens-before關聯:對volatile值的更改會在所有后續讀取該值的操作之前執行。因此,它確保了后序所有的讀取操作能夠看到之前的更改。

class VolatileFlag implements Runnable {
  private volatile boolean shouldStop;
  public void run() {
    while (!shouldStop) {
      //do smth
    }
    System.out.println("Stopped.");
  }
  void stop() {
    shouldStop = true;
  }
  public static void main(String[] args) throws InterruptedException {
    VolatileFlag flag = new VolatileFlag();
    Thread thread = new Thread(flag);
    thread.start();
    flag.stop();
    thread.join();
  }
}
Atomics

java.util.concurrent.atomic包中包含了一組支持在單一值上進行多種原子性操作的類,從而從加鎖中解脫出來。

使用AtomicXXX類,可以實現原子性的check-then-act操作:

class CheckThenAct {
  private final AtomicReference value = new AtomicReference<>();
  void initialize() {
    if (value.compareAndSet(null, "Initialized value")) {
      System.out.println("Initialized only once.");
    }
  }
}

AtomicIntegerAtomicLong都用increment/decrement操作:

class Increment {
  private final AtomicInteger state = new AtomicInteger();
  void advance() {
    int oldState = state.getAndIncrement();
    System.out.println("Advanced: "" + oldState + "" -> "" + (oldState + 1) + "".");
  }
}
如果你想要創建一個計數器,但是并不需要原子性的讀操作,可以使用LongAdder替代AtomicLong/AtomicIntegerLongAdder在多個單元格中維護該值,并在需要時對這些值同時遞增,從而在高并發的情況下性能更好。
ThreadLocal

在線程中包含數據并且不需要鎖定的一種方法是使用ThreadLocal存儲。從概念上將,ThreadLocal就好像是在每個線程中都有自己版本的變量。ThreadLocal常用來存儲只屬于線程自己的值,比如當前的事務以及其它資源。而且,它還能用來維護單個線程專有的計數器,統計或是ID生成器。

class TransactionManager {
  private final ThreadLocal currentTransaction 
      = ThreadLocal.withInitial(NullTransaction::new);
  Transaction currentTransaction() {
    Transaction current = currentTransaction.get();
    if (current.isNull()) {
      current = new TransactionImpl();
      currentTransaction.set(current);
    }
    return current;
  }
}


想要了解更多開發技術,面試教程以及互聯網公司內推,歡迎關注我的微信公眾號!將會不定期的發放福利哦~

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/70996.html

相關文章

  • 頭鷹深夜翻譯核心JAVA并發(二)

    摘要:前言上一篇文章請參考貓頭鷹的深夜翻譯核心并發一安全發布發布一個對象是指該對象的引用對當前的域之外也可見比如,從方法中獲取一個引用。任務的功能性接口表示一個沒有返回值的任務表示一個包含返回值的計算。 前言 上一篇文章請參考貓頭鷹的深夜翻譯:核心JAVA并發(一) 安全發布 發布一個對象是指該對象的引用對當前的域之外也可見(比如,從getter方法中獲取一個引用)。要確保一個對象被安全的發...

    Pink 評論0 收藏0
  • 頭鷹深夜翻譯:Volatile原子性, 可見性和有序性

    摘要:有可能一個線程中的動作相對于另一個線程出現亂序。當實際輸出取決于線程交錯的結果時,這種情況被稱為競爭條件。這里的問題在于代碼塊不是原子性的,而且實例的變化對別的線程不可見。這種不能同時在多個線程上執行的部分被稱為關鍵部分。 為什么要額外寫一篇文章來研究volatile呢?是因為這可能是并發中最令人困惑以及最被誤解的結構。我看過不少解釋volatile的博客,但是大多數要么不完整,要么難...

    Lionad-Morotar 評論0 收藏0
  • 頭鷹深夜翻譯:為何需要緩存以及如何實現緩存

    摘要:由于需要跨進程訪問網絡上的高速緩存,因此延遲,故障和對象序列化會導致性能下降。應用程序高速緩存會自動清除條目以保持其內存占用。緩存統計高速緩存統計信息可幫助識別高速緩存的運行狀況并提供有關高速緩存行為和性能的信息。 前言 這篇文章探索了現有的各種JAVA緩存基數,它們對各種場景下提高應用的性能起著重要的作用。 近十年來,信息技術極高的提升了業務流程,它已經成為了全球企業的戰略性方案。它...

    FuisonDesign 評論0 收藏0
  • 頭鷹深夜翻譯:JDK Vs. JRE Vs. JVM之間區別

    摘要:什么是為執行字節碼提供一個運行環境。它的實現主要包含三個部分,描述實現規格的文檔,具體實現和滿足要求的計算機程序以及實例具體執行字節碼。該類先被轉化為一組字節碼并放入文件中。字節碼校驗器通過字節碼校驗器檢查格式并找出非法代碼。 什么是Java Development Kit (JDK)? JDK通常用來開發Java應用和插件。基本上可以認為是一個軟件開發環境。JDK包含Java Run...

    blair 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<