您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關(guān)Java并發(fā)中如何實(shí)現(xiàn)線程封閉的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。
線程封閉基礎(chǔ)知識(shí)點(diǎn)
實(shí)現(xiàn)好的并發(fā)是一件困難的事情,所以很多時(shí)候我們都想躲避并發(fā)。避免并發(fā)最簡(jiǎn)單的方法就是線程封閉。什么是線程封閉呢?
就是把對(duì)象封裝到一個(gè)線程里,只有這一個(gè)線程能看到此對(duì)象。那么這個(gè)對(duì)象就算不是線程安全的也不會(huì)出現(xiàn)任何安全問題。實(shí)現(xiàn)線程封閉有哪些方法呢?
1:ad-hoc線程封閉
這是完全靠實(shí)現(xiàn)者控制的線程封閉,他的線程封閉完全靠實(shí)現(xiàn)者實(shí)現(xiàn)。也是最糟糕的一種線程封閉。所以我們直接把他忽略掉吧。
2:棧封閉
棧封閉是我們編程當(dāng)中遇到的最多的線程封閉。什么是棧封閉呢?簡(jiǎn)單的說就是局部變量。多個(gè)線程訪問一個(gè)方法,此方法中的
局部變量都會(huì)被拷貝一分兒到線程棧中。所以局部變量是不被多個(gè)線程所共享的,也就不會(huì)出現(xiàn)并發(fā)問題。所以能用局部變量就別用全局的變量,全局變量容易引起并發(fā)問題。
3:ThreadLocal封閉
使用ThreadLocal是實(shí)現(xiàn)線程封閉的最好方法,有興趣的朋友可以研究一下ThreadLocal的源碼,其實(shí)ThreadLocal內(nèi)部維護(hù)了一個(gè)Map,Map的key是每個(gè)線程的名稱,而Map的值就是我們要封閉的對(duì)象。每個(gè)線程中的對(duì)象都對(duì)應(yīng)著Map中一個(gè)值,也就是ThreadLocal利用Map實(shí)現(xiàn)了對(duì)象的線程封閉。這里就不說ThreadLocal的使用方法了,度娘一下便知。
1. 線程封閉
大多數(shù)的并發(fā)問題僅發(fā)生在我們想要在線程之間共享可變變量或可變狀態(tài)時(shí)。如果在多個(gè)線程之間操作共享變量,則所有線程都將能夠讀取和修改變量的值,從而出現(xiàn)意外或不正確的結(jié)果。一種簡(jiǎn)單的避免此問題的方式是不在線程之間共享數(shù)據(jù)。 這種技術(shù)稱為線程封閉,是在我們的應(yīng)用程序中實(shí)現(xiàn)線程安全的最簡(jiǎn)單方法之一。
Java 語言本身沒有任何強(qiáng)制執(zhí)行線程封閉的方法。線程封閉是通過不允許多個(gè)線程同時(shí)使用同一個(gè)狀態(tài)的方式的程序設(shè)計(jì)來實(shí)現(xiàn)的,因此由程序?qū)崿F(xiàn)強(qiáng)制執(zhí)行。 幾種類型的線程封閉,如下所示:
1.1 Ad-Hoc 線程封閉
Ad-hoc 線程封閉描述了線程封閉的方式,由開發(fā)人員或從事該項(xiàng)目的開發(fā)人員確保僅在單個(gè)線程內(nèi)使用此對(duì)象。 這種方式方法可用性不高,在大多數(shù)情況下應(yīng)該避免。
Ad-hoc 線程封閉下的一個(gè)特例適用于 volatile 變量。 只要確保 volatile 變量?jī)H從單個(gè)線程寫入,就可以安全地對(duì)共享 volatile 變量執(zhí)讀 - 改 - 寫操作。在這種情況下,您將修改限制在單個(gè)線程以防止競(jìng)爭(zhēng)條件,并且 volatile 變量的可見性保證確保其他線程看到最新值。
1.2 棧封閉
棧封閉將變量或?qū)ο蠓忾]在線程的棧中。這比 Ad-hoc 線程封閉強(qiáng)得多,因?yàn)樗ㄟ^定義堆棧本身中的變量狀態(tài)來進(jìn)一步限制對(duì)象的范圍。例如,請(qǐng)考慮以下代碼:
private long numberOfPeopleNamedJohn(List<Person> people) { List<Person> localPeople = new ArrayList<>(); localPeople.addAll(people); return localPeople.stream().filter(person -> person.getFirstName().equals("John")).count(); }
在上面的代碼中,我們傳遞一個(gè) person 對(duì)象的 list 但不直接使用它。 相反,我們創(chuàng)建自己的 list,該 list 是當(dāng)前正在執(zhí)行的線程的本地 list,并將變量 people中的所有 person 添加到 localPeople。由于我們僅在 numberOfPeopleNamedJohn方法中定義列表,這使得變量localPeople 受到堆棧隔離保護(hù),因?yàn)樗淮嬖谟谝粋€(gè)線程的堆棧上,因此任何其他線程都無法訪問它。這使得 localPeople 線程安全。 唯一需要注意的是,不應(yīng)該讓 localPeople 的作用于超過這個(gè)方法的范圍,以保證堆棧的隔離控制。在定義這個(gè)變量時(shí),應(yīng)該記錄或注釋為什么要定義這個(gè)變量,通常,只有在當(dāng)前開發(fā)人員的腦海中才不讓它超出方法的作用域,但是在將來,另一個(gè)開發(fā)人員可能會(huì)不知道為何如此設(shè)計(jì)而陷入困境。
1.3 ThreadLocal
ThreadLocal允許我們將每個(gè)線程 ID 與相應(yīng)對(duì)象的值相關(guān)聯(lián)。 它允許我們?yōu)椴煌木€程存儲(chǔ)不同的對(duì)象,并維護(hù)哪個(gè)對(duì)象對(duì)應(yīng)于哪個(gè)線程。它有 set 和 get 方法,這些方法為使用它的每個(gè)線程維護(hù)一個(gè)單獨(dú)的 value 副本。get() 方法總是返回從當(dāng)前正在執(zhí)行的線程傳遞給 set()的最新值。 我們來看一個(gè)例子:
public class ThreadConfinementUsingThreadLocal { public static void main(String[] args) { ThreadLocal<String> stringHolder = new ThreadLocal<>(); Runnable runnable1 = () -> { stringHolder.set("Thread in runnable1"); try { Thread.sleep(5000); System.out.println(stringHolder.get()); } catch (InterruptedException e) { e.printStackTrace(); } }; Runnable runnable2 = () -> { stringHolder.set("Thread in runnable2"); try { Thread.sleep(2000); stringHolder.set("string in runnable2 changed"); Thread.sleep(2000); System.out.println(stringHolder.get()); } catch (InterruptedException e) { e.printStackTrace(); } }; Runnable runnable3 = () -> { stringHolder.set("Thread in runnable3"); try { Thread.sleep(5000); System.out.println(stringHolder.get()); } catch (InterruptedException e) { e.printStackTrace(); } }; Thread thread1 = new Thread(runnable1); Thread thread2 = new Thread(runnable2); Thread thread3 = new Thread(runnable3); thread1.start(); thread2.start(); thread3.start(); } }
在上面的例子中,我們使用相同的 ThreadLocal 對(duì)象 stringHolder 執(zhí)行了三個(gè)線程。正如你在這里看到的,我們首先在 stringHolder 對(duì)象的每個(gè)線程中設(shè)置一個(gè)字符串,使其包含三個(gè)字符串。然后,經(jīng)過一些暫停后,我們只更改了第二個(gè)線程的值。 以下是該程序的輸出:
string in runnable2 changed Thread in runnable1 Thread in runnable3
正如您在上面的輸出中所看到的,線程2的字符串已更改,但線程1和線程3的字符串未受影響。如果我們?cè)趶?ThreadLocal 獲取特定線程的值之前沒有設(shè)置任何值,那么它返回null。 線程終止后,“ThreadLocal” 中特定于線程的對(duì)象就可以進(jìn)行垃圾回收了。
感謝各位的閱讀!關(guān)于“Java并發(fā)中如何實(shí)現(xiàn)線程封閉”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。