Java多線(xiàn)程編程一 并發(fā)編程基礎(chǔ)(上)

并發(fā)編程基礎(chǔ)一

進(jìn)程與線(xiàn)程

進(jìn)程:是并發(fā)執(zhí)行的程序在執(zhí)行過(guò)程中分配和管理資源的基本單位,是一個(gè)動(dòng)態(tài)概念,競(jìng)爭(zhēng)計(jì)算機(jī)系統(tǒng)資源的基本單位。

線(xiàn)程:是進(jìn)程的一個(gè)執(zhí)行單元,是進(jìn)程內(nèi)可調(diào)度實(shí)體。比進(jìn)程更小的獨(dú)立運(yùn)行的基本單位。線(xiàn)程也被稱(chēng)為輕量級(jí)進(jìn)程。

一個(gè)進(jìn)程有多個(gè)線(xiàn)程,多個(gè)線(xiàn)程共享進(jìn)程的堆和方法區(qū)資源,但是每個(gè)線(xiàn)程有自己的程序計(jì)數(shù)器和棧區(qū)域。

怎么創(chuàng)建線(xiàn)程?

  1. 實(shí)現(xiàn) Runnable Callable 接口

--實(shí)現(xiàn) run() 方法 無(wú)返回值,無(wú)參數(shù),難以傳參;

--實(shí)現(xiàn) call 方法 有返回值,可由 FutureTask<T> 類(lèi)型變量接收結(jié)果,無(wú)參數(shù),難以傳參。結(jié)果通過(guò) FutureTask 對(duì)象的 get() 方法獲取。

  1. 繼承 Thread 類(lèi)

--實(shí)現(xiàn) run() 方法 無(wú)返回值,無(wú)參數(shù),由于繼承特性,便于傳參;

  1. 使用線(xiàn)程池技術(shù)復(fù)用線(xiàn)程

Java中與線(xiàn)程相關(guān)的方法

Object 類(lèi)作為共享資源的相關(guān)實(shí)例方法

  1. wait(); 當(dāng)一個(gè)共享變量的 wait() 方法被調(diào)用時(shí),該調(diào)用線(xiàn)程會(huì)被阻塞掛起,直到

    ? (1).其他線(xiàn)程調(diào)用的該共享變量的 notify() 或 notifyAll() 方法喚醒;

    ? (2).其他線(xiàn)程調(diào)用了此線(xiàn)程的 interrupt() 方法,使此線(xiàn)程拋出異常而返回;

    wait(long timeout); 執(zhí)行此方法后,若該線(xiàn)程未在規(guī)定時(shí)間類(lèi)被其他線(xiàn)程喚醒,則會(huì)拋出異常返回。

    wait(long timeout , int nanos); 當(dāng) 0 < nanos <999999 ,timeout 增加 1;

  2. notify() 與 notifyAll(); 共享變量調(diào)用后,喚醒 一個(gè)/所有 被 wait 阻塞的線(xiàn)程,不放資源;

Thread 類(lèi)相關(guān)方法

獲取當(dāng)前的 Thread實(shí)例 Thread.currentThread();

  1. join(); 等待線(xiàn)程執(zhí)行結(jié)束的方法。

  2. static sleep(); 當(dāng)前線(xiàn)程等待時(shí)間后繼續(xù)執(zhí)行,不放 CPU;

  3. static yield(); 當(dāng)前線(xiàn)程禮讓?zhuān)缓笠黄饏⑴c對(duì) CPU 的競(jìng)爭(zhēng);

  4. void interrupt(); 將此線(xiàn)程標(biāo)記為被中斷狀態(tài)的線(xiàn)程

boolean isInterrupted();檢測(cè)是否被標(biāo)記為中斷狀態(tài)

static boolean interrupted();檢測(cè),且若檢測(cè)出中斷狀態(tài),將會(huì)清除中斷標(biāo)記;

死鎖

死鎖是指兩個(gè)或兩個(gè)以上的線(xiàn)程在執(zhí)行過(guò)程中,由于競(jìng)爭(zhēng)資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象。

死鎖產(chǎn)生的條件:

互斥條件:指進(jìn)程對(duì)所分配到的資源進(jìn)行排它性使用,即在一段時(shí)間內(nèi)某資源只由一個(gè)進(jìn)程占用。如果此時(shí)還有其它進(jìn)程請(qǐng)求資源,則請(qǐng)求者只能等待,直至占有資源的進(jìn)程用畢釋放。

請(qǐng)求和保持條件:指進(jìn)程已經(jīng)保持至少一個(gè)資源,但又提出了新的資源請(qǐng)求,而該資源已被其它進(jìn)程占有,此時(shí)請(qǐng)求進(jìn)程阻塞,但又對(duì)自己已獲得的其它資源保持不放。

不剝奪條件:指進(jìn)程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時(shí)由自己釋放。

環(huán)路等待條件:指在發(fā)生死鎖時(shí),必然存在一個(gè)進(jìn)程——資源的環(huán)形鏈,即進(jìn)程集合{P0,P1,P2,···,Pn}中的P0正在等待一個(gè) P1 占用的資源; P1 正在等待 P2 占用的資源,……, Pn 正在等待已被 P0。

一般能夠打破的死鎖條件為 請(qǐng)求并持有條件環(huán)路等待條件。

ThreadLocal 與 InheritableThreadLocal原理

當(dāng)使用ThreadLocal維護(hù)變量時(shí),ThreadLocal為每個(gè)使用該變量的線(xiàn)程提供獨(dú)立的變量副本,所以每一個(gè)線(xiàn)程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線(xiàn)程所對(duì)應(yīng)的副本。


//Thread 類(lèi)的成員

ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

讓我們看一下ThreadLocal類(lèi)存值的源碼


//ThreadLocal類(lèi)

public void set(T value) {

    //獲取當(dāng)前線(xiàn)程

    Thread t = Thread.currentThread();

    ThreadLocalMap map = getMap(t);

    if (map != null)

        map.set(this, value);

    else

        createMap(t, value);

}

//獲取線(xiàn)程類(lèi)存的threadLocals inheritableThreadLocals

ThreadLocalMap getMap(Thread t) {

    return t.threadLocals;

}

對(duì)于某一ThreadLocal來(lái)講,他的索引值i是確定的,在不同線(xiàn)程之間訪問(wèn)時(shí)訪問(wèn)的是不同的table數(shù)組的同一位置即都為table[i],只不過(guò)這個(gè)不同線(xiàn)程之間的table是獨(dú)立的。

對(duì)于同一線(xiàn)程的不同ThreadLocal來(lái)講,這些ThreadLocal實(shí)例共享一個(gè)table數(shù)組,然后每個(gè)ThreadLocal實(shí)例在table中的索引i是不同的。

ThreadLocal 不具繼承性,InheritableThreadLocal繼承自ThreadLocal,能夠解決ThreadLocal不能訪問(wèn)父線(xiàn)程本地變量的問(wèn)題。

ThreadLocal的內(nèi)存泄露問(wèn)題


/*

    ThreadLocal.ThreadLocalMap類(lèi)的Entry的鍵節(jié)點(diǎn)key是弱引用,會(huì)在垃圾回收器回收垃圾時(shí)被垃圾回收,而值是被Entry對(duì)象強(qiáng)引用,不會(huì)被回收,但不能再通過(guò)Entry鍵的key找到引用的值value。

*/

static class Entry extends WeakReference<ThreadLocal<?>> {

    Object value;

    Entry(ThreadLocal<?> k, Object v) {

        super(k);

        value = v;

    }

}

因?yàn)樯鲜龅脑?,在ThreadLocal這個(gè)類(lèi)的get()、set()、remove()方法,均有實(shí)現(xiàn)回收 key 為 null 的 Entry 的 value所占的內(nèi)存。所以,為了防止內(nèi)存泄露(沒(méi)法訪問(wèn)到的內(nèi)存),在不會(huì)再用ThreadLocal的線(xiàn)程任務(wù)末尾,調(diào)用一次 上述三個(gè)方法的其中一個(gè)即可。

例如:


private void remove(ThreadLocal<?> key) {

    Entry[] tab = table;

    int len = tab.length;

    int i = key.threadLocalHashCode & (len-1);

    //循環(huán)可能是因?yàn)榻鉀Qhash沖突方法為開(kāi)放地址

    for (Entry e = tab[i];

        e != null;

        e = tab[i = nextIndex(i, len)]) {

        if (e.get() == key) {

            e.clear();//清除引用



            //歷遍過(guò)程key為null的引用,value都會(huì)被賦值為null,釋放空間

            expungeStaleEntry(i);

            return;

        }

    }

}

方法


- void set(T value);設(shè)置當(dāng)前線(xiàn)程的線(xiàn)程局部變量的值。

- public T get();該方法返回當(dāng)前線(xiàn)程所對(duì)應(yīng)的線(xiàn)程局部變量。

- public void remove();將當(dāng)前線(xiàn)程局部變量的值刪除,目的是為了減少內(nèi)存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當(dāng)線(xiàn)程結(jié)束后,對(duì)應(yīng)該線(xiàn)程的局部變量將自動(dòng)被垃圾回收,所以顯式調(diào)用該方法清除線(xiàn)程的局部變量并不是必須的操作,但它可以加快內(nèi)存回收的速度。

- protected T initialValue();返回該線(xiàn)程局部變量的初始值,該方法是一個(gè)protected的方法,顯然是為了讓子類(lèi)覆蓋而設(shè)計(jì)的。這個(gè)方法是一個(gè)延遲調(diào)用方法,在線(xiàn)程第1次調(diào)用get()或set(Object)時(shí)才執(zhí)行,并且僅執(zhí)行1次。ThreadLocal中的缺省實(shí)現(xiàn)直接返回一個(gè)null。

ThreadLocalRandom

Random 類(lèi)的種子變量為支持 CAS 操作的變量,新的隨機(jī)數(shù)生成需要兩個(gè)步驟:

  1. 首先根據(jù)老種子生成新種子

  2. 然后根據(jù)新的種子生成新的隨機(jī)數(shù)

在多線(xiàn)程環(huán)境下,由于多個(gè)線(xiàn)程同時(shí)競(jìng)爭(zhēng)原子變量的更新操作,而 CAS 操作只能同時(shí)成功一個(gè),這會(huì)導(dǎo)致大量線(xiàn)程不斷進(jìn)行自旋重試,這會(huì)降低并發(fā)性。

與ThreadLocal原理相同,將種子存于線(xiàn)程中,每個(gè)線(xiàn)程有自己的種子,從而做到線(xiàn)程安全。

ThreadLocalRandom 類(lèi)通過(guò) ThreadLocalRandom.current() 來(lái)獲取當(dāng)前線(xiàn)程的隨機(jī)數(shù)生成器,此類(lèi)為工具類(lèi),繼承了 Random 類(lèi)但沒(méi)有使用其種子變量,而是調(diào)用 Thread 類(lèi)中實(shí)例的 threadLocalRandomSeed 變量。

通過(guò)讓每一個(gè)線(xiàn)程復(fù)制一份變量,使得每個(gè)線(xiàn)程對(duì)變量的操作是在本地副本上進(jìn)行,從而避免競(jìng)爭(zhēng),提高性能。

其他

用戶(hù)線(xiàn)程與守護(hù)線(xiàn)程的概念

參考書(shū)《Java并發(fā)編程之美》

ThreadLocal的內(nèi)存泄露問(wèn)題 參考:https://blog.csdn.net/yanluandai1985/article/details/82590336

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容