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

資訊專欄INFORMATION COLUMN

多線程中那些看不見的陷阱

phodal / 1405人閱讀

摘要:多線程編程就像一個(gè)沼澤,中間遍布各種各樣的陷阱。但是在多線程編程或者說是并發(fā)編程中,有非常多的陷阱被埋在底層細(xì)節(jié)當(dāng)中。線程池類中用于控制線程池狀態(tài)和線程數(shù)的控制變量就是一個(gè)類型的字段。

多線程編程就像一個(gè)沼澤,中間遍布各種各樣的陷阱。大多數(shù)開發(fā)者絕大部分時(shí)間都是在做上層應(yīng)用的開發(fā),并不需要過多地涉入底層細(xì)節(jié)。但是在多線程編程或者說是并發(fā)編程中,有非常多的陷阱被埋在底層細(xì)節(jié)當(dāng)中。如果不知道這些底層知識(shí),可能在編寫過程中完全意識(shí)不到程序已經(jīng)出現(xiàn)了漏洞,甚至在漏洞爆發(fā)之后也很難排查出具體原因進(jìn)而解決漏洞。雖然前面提到的漏洞聽起來很嚇人,但是相信通過我們逐步的抽絲剝繭,在最后一定能掌握大量的實(shí)用工具來幫助我們解決這些問題,實(shí)現(xiàn)可靠的并發(fā)程序。

閱讀本文需要了解并發(fā)的基本概念和Java多線程編程基礎(chǔ)知識(shí),還不了解的讀者可以參考一下下面兩篇文章:

并發(fā)的基本概念——當(dāng)我們?cè)谡f“并發(fā)、多線程”,說的是什么?

Java多線程編程基礎(chǔ)——這一次,讓我們完全掌握J(rèn)ava多線程

數(shù)據(jù)競爭問題

為了了解多線程程序有什么隱藏的陷阱,我們先來看一段代碼:

public class AccumulateWrong {

    private static int count = 0;

    public static void main(String[] args) throws Exception {
        Runnable task = new Runnable() {
            public void run() {
                for (int i = 0; i < 1000000; ++i) {
                    count += 1;
                }
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("count = " + count);
    }
}

這段代碼實(shí)現(xiàn)的基本功能就是在兩個(gè)線程中分別對(duì)一個(gè)整型累加一百萬次,那么我們期望的輸出應(yīng)該總共是兩百萬。但在我的電腦上運(yùn)行的結(jié)果只有1799369,而且每次都不一樣,相信在你的電腦上也會(huì)運(yùn)行得到一個(gè)不同的結(jié)果,但是肯定會(huì)達(dá)不到兩百萬。

這段代碼出現(xiàn)問題的原因就在于,我們?cè)趫?zhí)行count += 1;這行代碼時(shí),實(shí)際在CPU上運(yùn)行的會(huì)是多條指令:

獲取count變量的當(dāng)前值

計(jì)算count + 1的值

將count + 1的結(jié)果值存到count變量中

所以就有可能會(huì)發(fā)生下面的執(zhí)行順序:

t1 t2
獲取到count的值為100
計(jì)算100 + 1 = 101
獲取到count的值為100
把101保存到count變量中
計(jì)算100+ 1 = 101
把101保存到count變量中

這么一輪操作結(jié)束之后,雖然我們?cè)趦蓚€(gè)線程中分別對(duì)count累加了一次,總共是兩次,但是count的值只變大了1,這時(shí)結(jié)果就出現(xiàn)了問題。這種在多個(gè)線程中對(duì)共享數(shù)據(jù)進(jìn)行競爭性訪問的情況就被稱為數(shù)據(jù)競爭,可以理解為對(duì)共享數(shù)據(jù)的并發(fā)訪問會(huì)導(dǎo)致問題的情況就是數(shù)據(jù)競爭

那么我們?nèi)绾谓鉀Q這樣的數(shù)據(jù)競爭問題呢?

synchronized關(guān)鍵字

相信大多數(shù)讀者應(yīng)該都知道synchronized這個(gè)關(guān)鍵字,它可以被用在方法定義或者是塊結(jié)構(gòu)上,那么它到底能發(fā)揮怎樣的作用呢?我們把它以塊結(jié)構(gòu)的形式把count += 1;語句包圍起來看看。

for (int i = 0; i < 1000000; ++i) {
    synchronized (this) {
        count += 1;
    }
}

運(yùn)行之后可以看到,這次的輸出是兩百萬整了。在這里,synchronized發(fā)揮的作用就是讓兩個(gè)線程互斥地執(zhí)行count += 1;語句。所謂互斥也就是同一時(shí)間只能有一個(gè)線程執(zhí)行,如果另一個(gè)線程同時(shí)也要執(zhí)行的話則必須等到前一個(gè)線程完成操作退出synchronized語句塊之后才能進(jìn)入。

這種同一時(shí)間只能被一個(gè)線程訪問的代碼塊就被稱為臨界區(qū),而synchronized這樣的保護(hù)臨界區(qū)同時(shí)只能被一個(gè)線程進(jìn)入的機(jī)制就被稱為互斥鎖。當(dāng)一個(gè)線程因?yàn)榱硗庖粋€(gè)線程已經(jīng)獲取了鎖而陷入等待時(shí),我們可以稱該線程被這個(gè)鎖阻塞了。

在Java中,synchronized的背后是對(duì)象鎖,每個(gè)不同的對(duì)象都會(huì)對(duì)應(yīng)一個(gè)不同的鎖,同一個(gè)對(duì)象對(duì)應(yīng)同一個(gè)鎖。只有獲取同一個(gè)鎖才能達(dá)到互斥訪問的作用,如果兩個(gè)線程分別獲取不同的鎖,那么互相就不會(huì)影響了。所以在使用synchronized時(shí),區(qū)分背后對(duì)應(yīng)的是哪一個(gè)對(duì)象鎖就至關(guān)重要了。synchronized關(guān)鍵字可以被用在方法定義和塊結(jié)構(gòu)兩種情況中,具體對(duì)應(yīng)的鎖如下:

以塊結(jié)構(gòu)形式使用synchronized關(guān)鍵字,則獲取的就是synchronized關(guān)鍵字后小括號(hào)中的對(duì)象所對(duì)應(yīng)的鎖;

synchronized被標(biāo)記在實(shí)例方法上,則獲取的就是this引用指向?qū)ο笏鶎?duì)應(yīng)的鎖;

synchronized被標(biāo)記在類方法(靜態(tài)方法)上時(shí),獲取的就是方法所在類的“類對(duì)象”所對(duì)應(yīng)的鎖,這里的類對(duì)象就可以理解為是每個(gè)類一個(gè)用于存放靜態(tài)字段和靜態(tài)方法的對(duì)象。

因?yàn)?b>synchronized一定要有一個(gè)對(duì)應(yīng)的對(duì)象,所以我們自然不能將基本類型的變量傳入到synchronized后面的括號(hào)中。

ReentrantLock

在Java 5中JDK引入了java.util.concurrent包,也許大家都或多或少聽說過這個(gè)包,在這個(gè)包中提供了大量使用的并發(fā)工具類,例如線程池、鎖、原子數(shù)據(jù)類等等,對(duì)Java語言的并發(fā)編程易用性和實(shí)際效率產(chǎn)生了跨越性的提高。而ReentrantLock就是這個(gè)包中的一員。

ReentrantLock發(fā)揮的作用與synchronized相同,都是作為互斥鎖使用的。下面是把之前的累加代碼改為使用ReentrantLock鎖的版本:

final ReentrantLock lock = new ReentrantLock();

Runnable task = new Runnable() {
    public void run() {
        for (int i = 0; i < 1000000; ++i) {
            lock.lock();
            try {
                count += 1;
            } finally {
                lock.unlock();
            }
        }
    }
};

運(yùn)行之后的結(jié)果依然是兩百萬,說明ReentrantLock確實(shí)能起到保障互斥訪問臨界區(qū)的作用。但是既然ReentrantLocksynchronized的作用相同,而且從代碼來看使用synchronized還更方便,為什么還要專門定義一個(gè)ReentrantLock這樣的類呢?

上面的代碼中,雖然使用ReentrantLock還要專門寫一個(gè)try..finally塊來保證鎖釋放,比較麻煩,但是也能從中看到一個(gè)好處就是我們可以決定加鎖的位置和釋放鎖的位置。我們甚至可以在一個(gè)方法中加鎖,而在另一個(gè)方法中解鎖,雖然這樣做會(huì)有風(fēng)險(xiǎn)。相對(duì)于傳統(tǒng)的synchronizedReentrantLock還有下面的一些好處:

ReentrantLock可以實(shí)現(xiàn)帶有超時(shí)時(shí)間的鎖等待,我們可以通過tryLock方法進(jìn)行加鎖,并傳入超時(shí)時(shí)間參數(shù)。如果超過了超時(shí)時(shí)間還么有獲得鎖的話,那么就tryLock方法就會(huì)返回false;

ReentrantLock可以使用公平性機(jī)制,讓先申請(qǐng)鎖的線程先獲得鎖,防止線程一直等待鎖但是獲取不到;

ReentrantLock可以實(shí)現(xiàn)讀寫鎖等更豐富的類型。

更簡便的方式——AtomicInteger

java.util.concurrent包中,我們可以找到一個(gè)很有趣的子包atomic,在這個(gè)包中我們看到有很多以Atomic開頭的“包裝類型”,這些類會(huì)有什么用呢?我們先來看一下前面的累加程序使用AtomicInteger該如何實(shí)現(xiàn)。

public class AtomicIntegerDemo {

    private static AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) throws Exception {
        Runnable task = new Runnable() {
            public void run() {
                for (int i = 0; i < 1000000; ++i) {
                    count.incrementAndGet();
                }
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("count = " + count);
    }

}

運(yùn)行這個(gè)程序,我們也可以得到正確的結(jié)果兩百萬。在這個(gè)版本的代碼中我們主要改了兩處地方,一個(gè)是把count變量的類型修改為了AtomicInteger類型,然后把Runnable對(duì)象中的累加方式修改為了count.incrementAndGet()

AtomicInteger提供了原子性的變量值修改方式,原子性保證了整個(gè)累加操作可以被看成是一個(gè)操作,不會(huì)出現(xiàn)更細(xì)粒度的操作之間互相穿插導(dǎo)致錯(cuò)誤結(jié)果的情況。在底層AtomicInteger是基于硬件的CAS原語來實(shí)現(xiàn)的,CAS是“Compare and Swap”的縮寫,意思是在修改一個(gè)變量時(shí)會(huì)同時(shí)指定新值和舊值,只有在舊值等于變量的當(dāng)前值時(shí),才會(huì)把變量的值修改為新值。這個(gè)CAS操作在硬件層面是可以保證原子性的。

我們既可以用Atomic類來實(shí)現(xiàn)一些簡單的并發(fā)修改功能,也可以使用它來對(duì)一些關(guān)鍵的控制變量進(jìn)行控制,起到控制并發(fā)過程的目的。線程池類ThreadPoolExecutor中用于控制線程池狀態(tài)和線程數(shù)的控制變量ctl就是一個(gè)AtomicInteger類型的字段。

內(nèi)存可見性問題

看完了如何解決數(shù)據(jù)競爭問題,我們?cè)賮砜匆粋€(gè)略顯神奇的例子。

public class MemoryVisibilityDemo {

    private static boolean flag;

    public static void main(String[] args) throws Exception {

        for (int i = 0; i < 10000; ++i) {
            flag = false;
            final int no = i;

            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    flag = true;
                    System.out.println(String.format("No.%d loop, t1 is done.", no));
                }
            });

            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!flag) ;

                    System.out.println(String.format("No.%d loop, t2 is done.", no));
                }
            });

            t2.start();
            t1.start();

            t1.join();
            t2.join();
        }
    }

}

這段程序在我的電腦上輸出是這樣的:

No.0 loop, t2 is done.
No.0 loop, t1 is done.
No.1 loop, t1 is done.
No.1 loop, t2 is done.
No.2 loop, t2 is done.
No.2 loop, t1 is done.
No.3 loop, t2 is done.
No.3 loop, t1 is done.
No.4 loop, t1 is done.

在上面的程序輸出中我們可以看到,代碼中的循環(huán)是10000次,但是在程序輸出結(jié)果中到第五次就結(jié)束了。而且第五次運(yùn)行中只有t1執(zhí)行完了,t2的結(jié)束語句一直沒輸出。這說明程序被卡在了while (!flag) ;上,但是t1明明已經(jīng)運(yùn)行結(jié)束了,說明此時(shí)flag = true已經(jīng)執(zhí)行了,為什么t2還會(huì)被卡住呢?

這是因?yàn)?strong>內(nèi)存可見性在作祟,在計(jì)算機(jī)中,我們的存儲(chǔ)會(huì)分為很多不同的層次,大家比較常見的就是內(nèi)存和外存,外存就是比如磁盤、SSD這樣的持久性存儲(chǔ)。其實(shí)在內(nèi)存之上還有多個(gè)層次,較完整的計(jì)算機(jī)存儲(chǔ)體系從下到上依次有外存、內(nèi)存、“L3、L2、L1三層高速緩存”、寄存器這幾層。在這個(gè)存儲(chǔ)體系中從下到上是一個(gè)速度從慢到快的結(jié)構(gòu),越上層速度越快,所以當(dāng)CPU操作內(nèi)存數(shù)據(jù)時(shí)會(huì)盡量把數(shù)據(jù)讀取到內(nèi)存之上的高速緩存中再進(jìn)行讀寫。

所以如果程序想要修改一個(gè)變量的值,那么系統(tǒng)會(huì)先把新值寫到L1緩存中,之后在合適的時(shí)間才會(huì)將緩存中的數(shù)據(jù)寫回內(nèi)存當(dāng)中。雖然這樣的設(shè)置使系統(tǒng)的總體效率得到了提升,但是也帶來了一個(gè)問題,那就是L1、L2兩級(jí)高速緩存是核內(nèi)緩存,也就是說多核處理器的每一個(gè)核心都有自己獨(dú)立的L1、L2高速緩存。那么如果我們?cè)谝粋€(gè)核中運(yùn)行的線程上修改了變量的值而沒有寫回內(nèi)存的話,其他核心上運(yùn)行的線程就看不到這個(gè)變量的最新值了。

結(jié)合我們前面的程序例子,因?yàn)樾薷暮妥x取靜態(tài)變量flag的代碼在兩個(gè)不同的線程中,所以在多核處理器上運(yùn)行這段程序時(shí),就有可能在兩個(gè)不同的處理器核心上運(yùn)行這兩段代碼。最終就會(huì)導(dǎo)致線程t1雖然已經(jīng)把flag變量的值修改為true了,但是因?yàn)檫@個(gè)值還沒有寫回內(nèi)存,所以線程t2看到的flag變量的值仍然是false,這就是之前的代碼會(huì)被卡住的罪魁禍?zhǔn)住?/p>

那么我們?nèi)绾谓鉀Q這個(gè)問題呢?

volatile變量

最簡單的方式是使用volatile變量,即把flag變量標(biāo)記為volatile,如下所示:

private static volatile boolean flag;

這下程序就可以穩(wěn)定地跑完了,那么volatile做了什么解決了內(nèi)存可見性問題呢?根據(jù)編號(hào)為JSR-133的Java語言規(guī)范所定義的Java內(nèi)存模型(JMM)volatile變量保證了對(duì)該變量的寫入操作和在其之后的讀取操作之間存在同步關(guān)系,這個(gè)同步關(guān)系保證了對(duì)volatile變量的讀取一定可以獲取到該變量的最新值。在底層,對(duì)volatile變量的寫入會(huì)觸發(fā)高速緩存強(qiáng)制寫回內(nèi)存,該操作會(huì)使其他處理器核心中的同一個(gè)數(shù)據(jù)塊無效化,必須從內(nèi)存中重新讀取。Java內(nèi)存模型的具體內(nèi)容在下一節(jié)中會(huì)有簡單的介紹。

從上面的內(nèi)存可見性問題我們可以發(fā)現(xiàn),多線程程序中會(huì)出現(xiàn)的一些問題涉及一些非常底層的知識(shí),而且不了解的人是很難事先預(yù)防和事后排查的。所以對(duì)于希望真正掌握多線程編程的朋友來說,這必然會(huì)是一場非常奇妙與漫長的旅程,希望大家都能堅(jiān)持到最后。

Java內(nèi)存模型

Java語言規(guī)范中的JSR-133定義了一系列決定不同線程之間指令的邏輯順序,從而保證了不會(huì)出現(xiàn)內(nèi)存可見性和指令重排序所引發(fā)的并發(fā)問題,這對(duì)完全掌握多線程程序的正確性至關(guān)重要。

在程序中,我們一般會(huì)認(rèn)定程序語句是按代碼中的順序執(zhí)行的,比如下面這段代碼:

a = 0;
a = 1;
b = 2;
c = 3;

我們當(dāng)然會(huì)認(rèn)為程序的執(zhí)行順序是a = 0; -> a = 1; -> b = 2; -> c = 3;,但實(shí)際上會(huì)有兩種情況可能會(huì)破壞語句的執(zhí)行順序,一是編譯器對(duì)指令的重排序可能會(huì)導(dǎo)致語句的順序發(fā)生改變,二是前面提到的內(nèi)存可見性。

對(duì)于編譯器的指令重排序來說,雖然編譯器會(huì)保證單個(gè)線程內(nèi)語句的執(zhí)行效果與順序執(zhí)行相同,但是在上面的代碼中三個(gè)語句之間是沒有依賴關(guān)系的,任意順序執(zhí)行的效果都是相同的,所以編譯器是有可能對(duì)其中的語句進(jìn)行重排序的。在單線程程序中這當(dāng)然沒有問題,任意順序執(zhí)行上面代碼中的語句都是一樣的,但是在多線程情況下,問題就復(fù)雜了。如果另外一個(gè)線程在變量b的值變?yōu)?后會(huì)打印變量a的值,那么按我們的期望這段程序應(yīng)該打印出的1。但是如果b = 2;語句被重排序到了a = 1;之前和a = 0;之后,那么我們打印出的值就是0了。

對(duì)于內(nèi)存可見性,如果b = 2;對(duì)變量b的修改結(jié)果先于a = 1;寫回了內(nèi)存中。那么在另一個(gè)線程中,當(dāng)看到變量b的值變?yōu)?時(shí)還不能看到變量a的新值1,這同樣會(huì)導(dǎo)致程序打印出不符合我們期望的值。

從上面的介紹我們可以看出,在這個(gè)問題中最重要的是語句的執(zhí)行順序,在默認(rèn)情況下,我們可以保證單線程內(nèi)的執(zhí)行順序所產(chǎn)生的結(jié)果一定是符合我們的期望的,但一旦進(jìn)入多線程情況下,我們就不能做出這樣的保證了。那么我們?nèi)绾伪WC多個(gè)線程之間語句的執(zhí)行順序關(guān)系呢?這就要說到我們之前說到的Java內(nèi)存模型了。

Java內(nèi)存模型中定義了不同線程中的語句的順序關(guān)系,這被稱為Happens-Before關(guān)系,以下簡稱HB。這個(gè)關(guān)系指的是如果“操作A”HB于“操作B”,那么如果“操作A”確實(shí)在“操作B”之前已經(jīng)發(fā)生了,那么“操作B”一定會(huì)像在“操作A”之后發(fā)生一樣:看到“操作A”發(fā)生后所產(chǎn)生的所有結(jié)果,比如變量值的修改。如果“操作A”把變量a的值修改為了2,那么所有“操作B”都一定能看到變量a的值為2,不論是編譯器對(duì)指令的重排序還是不同處理器核心之間的內(nèi)存可見性都不能破壞這個(gè)結(jié)果。

正是因?yàn)檫@種指令執(zhí)行先后關(guān)系的核心就是看到之前執(zhí)行指令在內(nèi)存中體現(xiàn)的結(jié)果,所以這個(gè)規(guī)范才被稱為Java內(nèi)存模型

常用的Happens-Before關(guān)系規(guī)則:

同一個(gè)線程中,“先執(zhí)行的語句” HB于 “之后執(zhí)行的所有語句”;

“對(duì)volatile變量的寫操作” HB于 “對(duì)同一個(gè)變量的讀操作”;

“對(duì)鎖的釋放操作” HB于 “對(duì)同一個(gè)鎖的加鎖操作”;

“對(duì)Thread對(duì)象的start操作” HB于 “該線程任務(wù)中的第一行語句”;

“線程任務(wù)中的最后一行語句” HB于 “對(duì)該線程對(duì)應(yīng)的Thread對(duì)象的join操作”;

傳遞性規(guī)則:如果“操作B” HB于 “操作A”,“操作C” HB于 “操作B”,那么“操作C” 也HB于 “操作A”。

通過第一條規(guī)則我們就確定了單線程內(nèi)的語句的執(zhí)行順序,而通過規(guī)則2到規(guī)則4,我們就可以線程間確定具體的語句執(zhí)行順序了。最后的規(guī)則6傳遞性規(guī)則是整個(gè)規(guī)則體系的補(bǔ)充,利用這條規(guī)則我們就可以把規(guī)則1中的線程內(nèi)順序和規(guī)則2到4的線程間規(guī)則進(jìn)行結(jié)合,得到最終的完整順序體系了。

在下圖中,左邊一列和右邊一列分別是兩條不同的線程中執(zhí)行的語句及其順序。如果變量c是一個(gè)volatile變量,那么根據(jù)規(guī)則2,我們可以知道操作c = 3 HB于 操作print c,下圖中用紅線標(biāo)明了這個(gè)關(guān)系。所以根據(jù)JMM的定義,print c將可以看到變量c的值已經(jīng)被修改為3了,打印結(jié)果將是3,如果在print c語句下方繼續(xù)執(zhí)行對(duì)變量a和b的打印,那么結(jié)果必然分別是1和2。

但是我們不能保證右側(cè)的第一條print b語句一定會(huì)打印出2的值,即使它在時(shí)間上發(fā)生于b = 2之后。因?yàn)橹噶钪嘏判蚧蛘邇?nèi)存可見性問題都有可能會(huì)使它只能看到變量b在b = 2之前的原值。也就是說HB關(guān)系是沒辦法指定兩條線程中在HB關(guān)系之前的語句相互之間的順序關(guān)系的,在下圖的例子中就是print b并不能保證一定可以打印出值2,也有可能打印出變量b原來的值。

總結(jié)

在這篇文章中我們主要介紹了如何保證多線程程序的正確性,使運(yùn)行過程和結(jié)果符合我們的預(yù)期。通過對(duì)多線程程序正確性問題的探索,我們介紹了三種常用的線程同步方式,分別是鎖、CAS與volatile變量。其中,鎖有synchronized關(guān)鍵字和ReentrantLock兩種實(shí)現(xiàn)方式。

在這個(gè)過程中,我們深入到了計(jì)算機(jī)系統(tǒng)的底層,了解了計(jì)算機(jī)存儲(chǔ)體系結(jié)構(gòu)和volatile對(duì)高速緩存與內(nèi)存的影響。多線程編程是一個(gè)非常好的切入口,讓我們可以將以前曾經(jīng)學(xué)過的計(jì)算機(jī)理論知識(shí)與編程實(shí)踐結(jié)合起來,這種結(jié)合對(duì)非常多的高級(jí)知識(shí)領(lǐng)域都是至關(guān)重要的。

因?yàn)殄e(cuò)誤的程序是沒有價(jià)值的,所以對(duì)一個(gè)程序來說最重要的當(dāng)然是正確性。但是在實(shí)現(xiàn)了正確性的前提下,我們也必須要想辦法提升程序的性能。因?yàn)槎嗑€程的目標(biāo)就是通過多個(gè)線程的協(xié)作來提升程序的性能,如果達(dá)不到這個(gè)目標(biāo)的話我們辛辛苦苦寫的多線程代碼就沒有意義了。在下一篇文章中我們將會(huì)具體測試多線程程序的性能,通過發(fā)現(xiàn)多線程中那些會(huì)讓多線程程序運(yùn)行得比單線程程序更慢的性能陷阱,最終我們將找到解決這些陷阱的性能優(yōu)化方法。下一篇文章將在下周發(fā)布,有興趣的讀者可以關(guān)注一下。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/77514.html

相關(guān)文章

  • 線程編程完全指南

    摘要:在這個(gè)范圍廣大的并發(fā)技術(shù)領(lǐng)域當(dāng)中多線程編程可以說是基礎(chǔ)和核心,大多數(shù)抽象并發(fā)問題的構(gòu)思與解決都是基于多線程模型來進(jìn)行的。一般來說,多線程程序會(huì)面臨三類問題正確性問題效率問題死鎖問題。 多線程編程或者說范圍更大的并發(fā)編程是一種非常復(fù)雜且容易出錯(cuò)的編程方式,但是我們?yōu)槭裁催€要冒著風(fēng)險(xiǎn)艱辛地學(xué)習(xí)各種多線程編程技術(shù)、解決各種并發(fā)問題呢? 因?yàn)椴l(fā)是整個(gè)分布式集群的基礎(chǔ),通過分布式集群不僅可以大...

    mengera88 評(píng)論0 收藏0
  • 從0到1實(shí)現(xiàn)自己阻塞隊(duì)列(上)

    摘要:而且在大多數(shù)經(jīng)典的多線程編程資料中,阻塞隊(duì)列都是其中非常重要的一個(gè)實(shí)踐案例。甚至可以說只有自己動(dòng)手實(shí)現(xiàn)了一個(gè)阻塞隊(duì)列才能真正掌握多線程相關(guān)的。為什么會(huì)發(fā)生這種情況呢原因就是在我們實(shí)現(xiàn)的這個(gè)阻塞隊(duì)列中完全沒有線程同步機(jī)制,所以同時(shí)并發(fā)進(jìn)行的個(gè) 阻塞隊(duì)列不止是一道熱門的面試題,同時(shí)也是許多并發(fā)處理模型的基礎(chǔ),比如常用的線程池類ThreadPoolExecutor內(nèi)部就使用了阻塞隊(duì)列來保存等...

    niceforbear 評(píng)論0 收藏0
  • 線程程序加速指南

    摘要:在上文的代碼中,多線程累加的程序之所以會(huì)比單線程還慢得多就是因?yàn)樵陬愋偷撵o態(tài)變量上有兩個(gè)線程同時(shí)調(diào)用方法進(jìn)行累加,這就會(huì)導(dǎo)致在這個(gè)靜態(tài)變量上存在很嚴(yán)重的沖突。使用進(jìn)行任務(wù)中引入了一個(gè)新的多線程任務(wù)執(zhí)行框架,被稱為。 雖然對(duì)于一個(gè)計(jì)算機(jī)程序來說最重要的是正確性,如果一個(gè)程序沒辦法產(chǎn)出正確的結(jié)果,那么這個(gè)程序的價(jià)值就大打折扣了。但程序性能也是很重要的一個(gè)方面,如果程序運(yùn)行得太慢,那也會(huì)影響...

    xiao7cn 評(píng)論0 收藏0
  • Evil Python

    摘要:用將倒放這次讓我們一個(gè)用做一個(gè)小工具將動(dòng)態(tài)圖片倒序播放發(fā)現(xiàn)引力波的機(jī)構(gòu)使用的包美國科學(xué)家日宣布,他們?nèi)ツ暝率状翁綔y到引力波。宣布這一發(fā)現(xiàn)的,是激光干涉引力波天文臺(tái)的負(fù)責(zé)人。這個(gè)機(jī)構(gòu)誕生于上世紀(jì)年代,進(jìn)行引力波觀測已經(jīng)有近年。 那些年我們寫過的爬蟲 從寫 nodejs 的第一個(gè)爬蟲開始陸陸續(xù)續(xù)寫了好幾個(gè)爬蟲,從爬拉勾網(wǎng)上的職位信息到爬豆瓣上的租房帖子,再到去爬知乎上的妹子照片什么的,爬蟲...

    Turbo 評(píng)論0 收藏0
  • 企業(yè)云計(jì)算道路隱藏著各種陷阱和困難

    摘要:但在現(xiàn)實(shí)中,企業(yè)云端漫步的道路從來都不是康莊大道,而是隱藏著各種陷阱和困難。如果企業(yè)選擇云計(jì)算服務(wù),那么這些技術(shù)人員很多將被解聘。 如今,越來越多的大企業(yè)禁不住應(yīng)用軟件廠商的誘惑,開始考慮將原來運(yùn)行在私有數(shù)據(jù)中心的大型應(yīng)用系統(tǒng)遷移到云端。而關(guān)于云計(jì)算的商業(yè)價(jià)值,形形色色 的云計(jì)算服務(wù)商通常會(huì)告訴企業(yè):云計(jì)算可以幫助企業(yè)節(jié)省成本,增強(qiáng)IT系統(tǒng)與業(yè)務(wù)靈活性,加快應(yīng)用部署速度,增強(qiáng)業(yè)務(wù)創(chuàng)新能力…相...

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

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

0條評(píng)論

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