java线程知识梳理_Java多线程——多线程相关知识的逻辑关系梳理
1 學(xué)習(xí)多線程知識(shí)的根本目標(biāo)
多線程知識(shí)的根本目標(biāo)是:設(shè)計(jì)穩(wěn)健的并發(fā)程序。
當(dāng)然,本文無(wú)法回答這個(gè)實(shí)踐性很強(qiáng)的問題(這與具體的業(yè)務(wù)相關(guān),涉及到具體的策略),本文主要闡述相關(guān)知識(shí)之間的關(guān)系,希望初學(xué)者不要迷失在多線程工具類的API接口中。
2 并發(fā)程序的三大宏觀問題
線程安全性問題、性能問題、活躍性問題。 三者的關(guān)系是,在設(shè)計(jì)并發(fā)程序過程中要首先保證線程安全,在線程安全的基礎(chǔ)上努力提升程序性能,在保證線程安全與提升性能時(shí)避免引入活躍性問題。
線程安全是最重要的,設(shè)計(jì)并發(fā)程序是為了提升程序的性能,但是永遠(yuǎn)不要忘記性能是建立在安全的基礎(chǔ)上的;而在保證安全性(如加鎖、同步等方法)及優(yōu)化并發(fā)性能(如通過鎖分解、鎖分段等方法)過程中,可能會(huì)引入活躍性問題(如死鎖、饑餓、活鎖、糟糕的響應(yīng)性等問題)。 本文主要闡述線程安全問題的知識(shí)。
3 線程安全性
3.1 線程安全性的定義
當(dāng)多個(gè)線程訪問某個(gè)類時(shí),這個(gè)類始終都能表現(xiàn)出正確的行為,那么就稱這個(gè)類是線程安全的。最核心的概念就是正確性。正確性的含義是,某個(gè)類的行為與其規(guī)范完全一致。
就像你要設(shè)計(jì)一道菜,那么組成這道菜的食材和調(diào)料一定是可控的,即你能確切的描述對(duì)這些食材和調(diào)料操作的結(jié)果,比如放油菜會(huì)香,放鹽菜會(huì)咸,并且知道菜快熟時(shí)再放鹽,只有對(duì)構(gòu)成這道菜的所有元素都清楚明晰,設(shè)計(jì)出的菜才符合設(shè)想。設(shè)計(jì)程序時(shí)也一樣,只有使用的各個(gè)變量和函數(shù)是可控的,設(shè)計(jì)出的程序才能按照設(shè)計(jì)運(yùn)行。而多線程的使用,可能會(huì)使得組成程序的某些變量和行為變得不可控。
3.2 為什么多線程會(huì)帶來(lái)線程安全性問題
要闡明線程安全性問題需要深入到JAVA內(nèi)存模型,這里暫時(shí)不引入,這里用兩個(gè)生活中的例子來(lái)闡述線程安全性問題的本質(zhì)(原子性問題和可見性問題)。
【例1】以炒菜為例。其中有個(gè)操作是放鹽,放鹽的操作可分解為三個(gè)步驟“找到鹽罐→檢查鹽罐裝的是鹽→菜快熟的時(shí)候?qū)Ⅺ}倒入菜中”。如果只有一個(gè)人在使用鹽罐,那么不會(huì)有什么問題。但是,如果在你“檢查鹽罐中裝的是鹽”之后,將鹽倒入菜中之前,另一個(gè)人拿走了鹽罐,裝了三氯氰胺奶粉,然后將鹽罐放回。這個(gè)調(diào)換過程你并不知道,最后你將調(diào)換過的三氯氰胺奶粉倒入了菜中,結(jié)果可想而知。這就是線程安全性問題的原子性問題。
從內(nèi)存模型理解原子性問題:juejin.im/post/684490…
【例2】以A和B協(xié)作煮米飯為例。有三個(gè)房間(廚房、控火室、觀察室),廚房有個(gè)鍋煮著米飯(鍋上有張紙寫著“已熟”或者“未熟”),A在控火室負(fù)責(zé)關(guān)火(當(dāng)看到鍋上的紙寫著“已熟”時(shí)關(guān)火),B在觀察室負(fù)責(zé)觀察米的狀態(tài)并修改鍋上貼紙的狀態(tài)字(已熟/未熟);A和B不在一個(gè)房間,無(wú)法直接交流;B隔一會(huì)觀察下鍋里的米,如果米熟了就在一張紙上寫“已熟”,未熟就寫“未熟”,B很忙,寫完字后將紙貼在了自己門上,未貼在廚房的鍋上。最終結(jié)果是,A看到鍋上的狀態(tài)字一直是“未熟”,所以不關(guān)火,最終米飯糊了。這就是可見性問題。
從內(nèi)存模型理解可見性問題:juejin.im/post/684490…
3.3 解決原子性問題和內(nèi)存可見性的核心
要編寫線程安全的代碼,其核心在于要對(duì)狀態(tài)(變量)訪問操作進(jìn)行管理,特別是對(duì)共享的和可變的狀態(tài)的訪問。
共享意味著可由多個(gè)線程訪問操作,可變意味著可以被修改。
上述例1中,如果鹽罐不共享即其他人不能使用,則不會(huì)有線程安全性問題。如果規(guī)定不能往鹽罐中裝入,只能從鹽罐中取出,那么不管多少個(gè)人可以操作鹽罐,鹽罐中始終是鹽,沒有線程安全性問題;同樣,如果一個(gè)變量不能被修改,那么不管多少個(gè)線程操作這個(gè)變量,也不會(huì)帶來(lái)線程安全性問題。
上述例2中,如果強(qiáng)制B在修改狀態(tài)字后,將紙貼在鍋上,那么就可以讓A看到米飯的真正狀態(tài),就可以及時(shí)關(guān)掉火,蒸出香噴噴的米飯。
那么,為了解決原子性問題和內(nèi)存可見性問題,怎么對(duì)共享的且可變的變量進(jìn)行管理?
除非需要某個(gè)域是可變的,否則應(yīng)將其聲明為final域(即聲明為不可變的)
將對(duì)共享變量的并行操作轉(zhuǎn)換為串行操作(如同步機(jī)制、將變量委托給同步容器進(jìn)行管理)
將共享變量轉(zhuǎn)換為不共享變量(如線程封閉(棧封閉、ThreadLocal類等))
如果某個(gè)操作需要以原子方式執(zhí)行,那么就想辦法保證這個(gè)操作是原子方式執(zhí)行(如給操作加鎖)
對(duì)于需要保證內(nèi)存可見性的變量,可以強(qiáng)制線程從主內(nèi)存而不是緩存中讀取變量,在線程存儲(chǔ)變量時(shí)強(qiáng)制存取到主內(nèi)存中。(如加鎖,聲明volatile變量)
切記,核心是管理共享的可變狀態(tài),鎖(內(nèi)置鎖、Lock鎖)、同步容器類(Vector、Hashtable等)、并發(fā)容器類(ConcurrentHashMap、CopyOnWriteArrayList)、同步工具類(閉鎖、信號(hào)量、柵欄)、線程池等都只是是管理共享的可變狀態(tài)的工具。
總結(jié)
以上是生活随笔為你收集整理的java线程知识梳理_Java多线程——多线程相关知识的逻辑关系梳理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 主从二进制日志_Mysql-
- 下一篇: 套利怎么操作