亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

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

Richard_Gao / 607人閱讀

摘要:簡(jiǎn)介從創(chuàng)建以來,就支持核心的并發(fā)概念如線程和鎖。這篇文章會(huì)幫助從事多線程編程的開發(fā)人員理解核心的并發(fā)概念以及如何使用它們。請(qǐng)求操作系統(tǒng)互斥,并讓操作系統(tǒng)調(diào)度程序處理線程停放和喚醒。

簡(jiǎn)介

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

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

概念
原子性:原子操作是指該系列操作要么全部執(zhí)行,要么全部不執(zhí)行,因此不存在部分執(zhí)行的狀態(tài)。
可見性:一個(gè)線程能夠看見另一個(gè)線程所帶來的改變。
競(jìng)爭(zhēng)情況

當(dāng)多個(gè)線程在一個(gè)共享的資源上執(zhí)行一組操作時(shí),會(huì)產(chǎn)生競(jìng)爭(zhēng)。根據(jù)各個(gè)線程執(zhí)行操作的順序可能產(chǎn)生多個(gè)不同結(jié)果。下面的代碼不是線程安全的,value可能會(huì)被初始化多次,因?yàn)?b>check-then-act型(先判斷是否為null,然后初始化)的惰性初始化并非原子性操作

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

當(dāng)兩個(gè)或多個(gè)線程在沒有同步的情況下試圖訪問同一個(gè)非final變量時(shí),會(huì)產(chǎn)生數(shù)據(jù)沖突。不使用同步可能使數(shù)據(jù)的改變對(duì)別的線程不可見,從而可能讀取過期的數(shù)據(jù),并導(dǎo)致如無限循環(huán),數(shù)據(jù)結(jié)構(gòu)損壞和不準(zhǔn)確的計(jì)算等后果。下面這段代碼可能會(huì)導(dǎo)致無限循環(huán),因?yàn)樽x者線程可能永遠(yuǎn)都沒有看到寫入者線程做出的更改:

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內(nèi)存模型:happens-before關(guān)系

JAVA內(nèi)存模型是根據(jù)讀寫字段等操作來定義的,并在控制器上進(jìn)行同步。操作根據(jù)happens-before關(guān)聯(lián)排序,這解釋了一個(gè)線程何時(shí)能夠看到另一個(gè)線程操作的結(jié)果,以及是什么構(gòu)成了一個(gè)同步良好的程序。

happens-before關(guān)聯(lián)有以下屬性:

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

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

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

寫入final型變量的操作在發(fā)布該對(duì)象的引用之前執(zhí)行

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

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

標(biāo)注的同步功能 synchronized關(guān)鍵字

synchronized關(guān)鍵字用來防止不同的線程同時(shí)進(jìn)入一段代碼。它確保了你的操作的原子性,因?yàn)槟阒挥蝎@得了這段代碼的鎖才能進(jìn)入這段代碼,使得該鎖所保護(hù)的數(shù)據(jù)可以在獨(dú)占模式下操作。除此以外,它還確保了別的線程在獲得了同樣的鎖之后,能夠觀察到之前線程的操作。

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

synchronized關(guān)鍵字也可以在方法層上聲明。

靜態(tài)方法:將持有該方法的類作為加鎖對(duì)象
非靜態(tài)方法:加鎖this指針

鎖是可重入的。所以如果一個(gè)線程已經(jīng)持有了該鎖,它可以一直訪問該鎖下的任何內(nèi)容:

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.");
  }
}

爭(zhēng)用程度影響如何獲得控制器:

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

wait/notify/notifyAll方法在Object類中聲明。wait方法用來將線程狀態(tài)改變?yōu)?b>WAITING或是TIMED_WAITING(如果傳入了超時(shí)時(shí)間值)。要想喚醒一個(gè)線程,下列的操作都可以實(shí)現(xiàn):

另一個(gè)線程調(diào)用notify方法,喚醒在控制器上等待的任意的一個(gè)線程

另一個(gè)線程調(diào)用notifyAll方法,喚醒在該控制器上等待的所有線程

Thread#interrupt方法被調(diào)用,在這種情況下,會(huì)拋出InterruptedException

最常用的一個(gè)模式是一個(gè)條件性循環(huán):

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

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

總是在一個(gè)條件性循環(huán)中等待,從而解決如果另一個(gè)線程在wait開始之前滿足條件并且調(diào)用了notifyAll而導(dǎo)致的順序問題。而且它還防止線程由于偽喚起繼續(xù)執(zhí)行。

時(shí)刻確保你在調(diào)用notify/notifyAll之前已經(jīng)滿足了等待條件。如果不這樣的話,將只會(huì)發(fā)出一個(gè)喚醒通知,但是在該等待條件上的線程永遠(yuǎn)無法跳出其等待循環(huán)。

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

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

假設(shè)現(xiàn)在有兩個(gè)線程分別同時(shí)調(diào)用waitForConditionsatisfyCondition(),而調(diào)用satisfyCondition的方法先調(diào)用完成,并且發(fā)出了notifyAll通知。鑒于waitForCondition方法根本沒有進(jìn)入wait方法,因此它就錯(cuò)過了這個(gè)解掛信號(hào),從而永遠(yuǎn)無法被喚醒。

這時(shí)你可能會(huì)想,那就使用if判斷一下條件唄,如果條件還沒滿足,就進(jìn)入掛起狀態(tài),一旦接收到信號(hào),就可以直接執(zhí)行后序程序。代碼如下:

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

那讓我們?cè)偌僭O(shè)這個(gè) 方法中還存在另一個(gè)condition,并且也有其對(duì)應(yīng)的等待和喚醒方法。假設(shè)這時(shí)satisfyConsition2被滿足并發(fā)出nofityAll喚醒所有等待的線程,那么waitForConditionwaitForCondition2都將會(huì)被喚醒繼續(xù)執(zhí)行。而waitForCondition的條件并沒有被滿足!

因此在條件中循環(huán)等待信號(hào)是有必要的。

volatile關(guān)鍵字

volatile關(guān)鍵字解決了可見性問題,并且使值的更改原子化,因?yàn)檫@里存在一個(gè)happens-before關(guān)聯(lián):對(duì)volatile值的更改會(huì)在所有后續(xù)讀取該值的操作之前執(zhí)行。因此,它確保了后序所有的讀取操作能夠看到之前的更改。

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包中包含了一組支持在單一值上進(jìn)行多種原子性操作的類,從而從加鎖中解脫出來。

使用AtomicXXX類,可以實(shí)現(xiàn)原子性的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) + "".");
  }
}
如果你想要?jiǎng)?chuàng)建一個(gè)計(jì)數(shù)器,但是并不需要原子性的讀操作,可以使用LongAdder替代AtomicLong/AtomicIntegerLongAdder在多個(gè)單元格中維護(hù)該值,并在需要時(shí)對(duì)這些值同時(shí)遞增,從而在高并發(fā)的情況下性能更好。
ThreadLocal

在線程中包含數(shù)據(jù)并且不需要鎖定的一種方法是使用ThreadLocal存儲(chǔ)。從概念上將,ThreadLocal就好像是在每個(gè)線程中都有自己版本的變量。ThreadLocal常用來存儲(chǔ)只屬于線程自己的值,比如當(dāng)前的事務(wù)以及其它資源。而且,它還能用來維護(hù)單個(gè)線程專有的計(jì)數(shù)器,統(tǒng)計(jì)或是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;
  }
}


想要了解更多開發(fā)技術(shù),面試教程以及互聯(lián)網(wǎng)公司內(nèi)推,歡迎關(guān)注我的微信公眾號(hào)!將會(huì)不定期的發(fā)放福利哦~

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/70996.html

相關(guān)文章

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

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

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

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

    Lionad-Morotar 評(píng)論0 收藏0
  • 頭鷹深夜翻譯:為何需要緩存以及如何實(shí)現(xiàn)緩存

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

    FuisonDesign 評(píng)論0 收藏0
  • 頭鷹深夜翻譯:JDK Vs. JRE Vs. JVM之間區(qū)別

    摘要:什么是為執(zhí)行字節(jié)碼提供一個(gè)運(yùn)行環(huán)境。它的實(shí)現(xiàn)主要包含三個(gè)部分,描述實(shí)現(xiàn)規(guī)格的文檔,具體實(shí)現(xiàn)和滿足要求的計(jì)算機(jī)程序以及實(shí)例具體執(zhí)行字節(jié)碼。該類先被轉(zhuǎn)化為一組字節(jié)碼并放入文件中。字節(jié)碼校驗(yàn)器通過字節(jié)碼校驗(yàn)器檢查格式并找出非法代碼。 什么是Java Development Kit (JDK)? JDK通常用來開發(fā)Java應(yīng)用和插件?;旧峡梢哉J(rèn)為是一個(gè)軟件開發(fā)環(huán)境。JDK包含Java Run...

    blair 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<