国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

ArrayList為什么不是線程安全的,如何保證線程安全?

這篇具有很好參考價(jià)值的文章主要介紹了ArrayList為什么不是線程安全的,如何保證線程安全?。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

一下詳細(xì)分析原因

官方曰,線程安全就是多線程訪問時(shí),采?了加鎖機(jī)制,當(dāng)?個(gè)線程訪問該類的某個(gè)數(shù)據(jù)時(shí),進(jìn)?保護(hù),其他線程不能進(jìn)?訪問直到該線程讀取完,其他線程才可使?。不會出現(xiàn)數(shù)據(jù)不?致或者數(shù)據(jù)污染。線程不安全就是不提供數(shù)據(jù)訪問保護(hù),有可能出現(xiàn)多個(gè)線程先后更改數(shù)據(jù)造成所得到的數(shù)據(jù)是臟數(shù)據(jù)。說白了,線程安全就是多個(gè)線程修改同一個(gè)變量的時(shí)候,修改的結(jié)果需要和單線程修改的結(jié)果相同。如果修改的結(jié)果和預(yù)期不符,那就是線程不安全。

代碼例子:

ArrayList為什么不是線程安全的,如何保證線程安全?

結(jié)果為0,為什么不是1?

解釋:因?yàn)閒or循環(huán)里新增了一個(gè)新的線程,來負(fù)責(zé)向list里add一個(gè)元素。但是我們打印的list.size 是主線程。如果在新的線程 new Thread 沒執(zhí)行完add 方法, 主線程就執(zhí)行打印的代碼,那么就是 0啊 。所以就是說,主線程等一等,讓 for循環(huán)里面的新的線程 new Thread 先插入數(shù)據(jù)。

ArrayList為什么不是線程安全的,如何保證線程安全?

可以看到結(jié)果與期望值1是一致的。

情況①:

正常運(yùn)行的情況,可以看到 10個(gè)線程 不爭不搶 :

ArrayList為什么不是線程安全的,如何保證線程安全?

情況②:

多個(gè)線程搶占資源,有競爭,(僅僅對于往list塞數(shù)據(jù)這個(gè)動作來說)

ArrayList為什么不是線程安全的,如何保證線程安全?

情況③:

出現(xiàn) ‘不安全’情況了 ,多線程操作 ArrayList 導(dǎo)致出現(xiàn) add賦值 出現(xiàn) null

ArrayList為什么不是線程安全的,如何保證線程安全?

出現(xiàn) null 情景分析 ,先看看源碼:

ArrayList為什么不是線程安全的,如何保證線程安全?

Object[] elementData : 保存所有元素值的 數(shù)組

size : elementData中存儲的元素個(gè)數(shù)

再看看 add 函數(shù)的源碼 :

ArrayList為什么不是線程安全的,如何保證線程安全?
ArrayList為什么不是線程安全的,如何保證線程安全?

ensureExplicitCapacity 函數(shù):

將當(dāng)前的新元素加到列表后面,判斷列表的 elementData 數(shù)組的大小是否滿足。如果 size + 1 的這個(gè)需求長度大于 elementData 這個(gè)數(shù)組的長度,那么就要對這個(gè)數(shù)組進(jìn)行擴(kuò)容。

elementData[size++] = e

e是傳入的值, 把這個(gè)值賦值在 elementData數(shù)組的 size++ 位置 。

很顯然,這兩步?jīng)]有和在一塊操作。

也就說如果出現(xiàn)這個(gè)擴(kuò)容的觸發(fā)和后面賦值并發(fā)情況 ,那么就有不安全問題產(chǎn)生。

ArrayList是基于數(shù)組實(shí)現(xiàn),數(shù)組大小一旦確定就無法更改。

ArrayList的擴(kuò)容: 將舊數(shù)組容器的元素拷貝到新大小的數(shù)組中(Arrays.copyOf函數(shù))。

ArrayList為什么不是線程安全的,如何保證線程安全?

通過new ArrayList<>()實(shí)例的對象初始化的大小是0,所以第一次插入就肯定會觸發(fā)擴(kuò)容。如下代碼:

ArrayList為什么不是線程安全的,如何保證線程安全?

那什么時(shí)候DEFAULT_CAPACITY = 10 默認(rèn)值起作用呢?其實(shí)官方有注釋:

ArrayList為什么不是線程安全的,如何保證線程安全?

添加第一個(gè)元素時(shí),任何elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList都將擴(kuò)展為DEFAULT_CAPACITY。

第一個(gè)數(shù)據(jù)是 null (其實(shí)應(yīng)該稱為 執(zhí)行擴(kuò)容操作,并發(fā)導(dǎo)致出現(xiàn)null值 )分析 :

第一個(gè)線程A 插入數(shù)據(jù)時(shí)屬于首次add ,發(fā)現(xiàn)需要擴(kuò)容 , 線程A 去擴(kuò)容去了。

然后 我們是多線程操作場景, for循環(huán)第二次,觸發(fā)new第二個(gè)線程B來了,線程B去add的時(shí)候,因?yàn)榫€程A第一次擴(kuò)容可能并沒完成,所以導(dǎo)致線程B 擴(kuò)容所拿到list的elementDate是舊的,并不是線程A第一次擴(kuò)容后對象, 線程B 拿到的 size還是 0 ,所以線程B 也認(rèn)為自己是第一次add ,也需要擴(kuò)容。

想一下 A 、B 線程的并發(fā) 一起進(jìn)入擴(kuò)容場景:

那么線程A 是第一次add的時(shí)候,他知道他要去擴(kuò)容,他自己擴(kuò)容完,自己整了個(gè)list的新elementDate ,然后 就開始賦值 elementDate[size++] = A的UUID值。

在線程A這個(gè)操作的過程中,線程 B 在做什么?

線程 B一開始 不巧也是以為要擴(kuò)容,他拿著一個(gè)舊的 list的elementDate 也整了一個(gè)新的數(shù)組 ,

然后把 整個(gè) list的 elementDate 引用指向 B線程自己弄出來的對象

this.elementData = B新構(gòu)建的對象(這對象全部值為null);

然后做什么?

ArrayList為什么不是線程安全的,如何保證線程安全?

然后 線程B 開始執(zhí)行 elementDate[size++] = B的UUID值。

線程A 的值賦值在他創(chuàng)建出來的 elementDate 里面,然后觸發(fā) size++ 。

但是線程 B 呢, 把 this.elementData 指向了自己的新弄出來的, 所以 A 的值無情被拋棄,但是線程 B 開始賦值的時(shí)候,

看看這個(gè)size在源碼里的情況:

size是大家共用的,size 被線程A 加1了 ,所以就出現(xiàn) 線程 B 賦值的時(shí)候執(zhí)行 elementDate[size++] = B的UUID值,出來的結(jié)果是:

[null , B的UUID值]

情況④:

java.util.ConcurrentModificationException 并發(fā)沖突

ArrayList為什么不是線程安全的,如何保證線程安全?

直接定位報(bào)錯(cuò)函數(shù):

ArrayList為什么不是線程安全的,如何保證線程安全?

modCount是修改記錄數(shù),expectedModCount是期望修改記錄數(shù);

初始化的時(shí)候 expectedModCount=modCount ;

ArrayList的add函數(shù)、remove函數(shù) 操作都有對modCount++操作,當(dāng)expectedModCount和modCount值不相等, 那就會報(bào)并發(fā)錯(cuò)誤了

怎么辦? 怎么安全起來?

1.使用 Vector :

List<String> resultList = newVector<>();

看看vector怎么保證安全的,add 方法synchronized加鎖實(shí)現(xiàn)

ArrayList為什么不是線程安全的,如何保證線程安全?

主意:Vector是一個(gè)線程安全的列表,底層采用數(shù)組實(shí)現(xiàn)。其線程安全的實(shí)現(xiàn)方式非常粗暴:Vector大部分方法和ArrayList都是相同的,只是加上了synchronized關(guān)鍵字,這種方式嚴(yán)重影響效率,因此,不再推薦使用Vector了。JAVA官方文檔中這樣描述:如果不需要線程安全性,推薦使用ArrayList替代Vector。

2.使用 Collections里面的synchronizedList:

List<String> resultList =Collections.synchronizedList(new ArrayList<>());

synchronizedList同步塊保證安全的:

ArrayList為什么不是線程安全的,如何保證線程安全?

但是迭代器未加鎖,需要手動實(shí)現(xiàn)同步:

ArrayList為什么不是線程安全的,如何保證線程安全?

所以使用Collections.synchronizedList注意兩個(gè)地方:

1.迭代操作必須加鎖,可以使用synchronized關(guān)鍵字修飾;

2.synchronized持有的監(jiān)視器對象必須是synchronized (list),即包裝后的list,使用其他對象如synchronized (new Object())會使add,remove等方法與迭代方法使用的鎖不一致,無法實(shí)現(xiàn)完全的線程安全性。

3.使用 CopyOnWriteArrayList :

List<String> resultList = new CopyOnWriteArrayList();

CopyOnWriteArrayList 使用ReentrantLock保證安全的:

ArrayList為什么不是線程安全的,如何保證線程安全?

CopyOnWriteArrayList 的set 也是上鎖:

ArrayList為什么不是線程安全的,如何保證線程安全?

1.set add remove 都選擇使用了Arrays.copyOf復(fù)制操作

2.get 多線程過程讀取數(shù)據(jù)不是實(shí)時(shí),那就可能出現(xiàn) 數(shù)據(jù)不一致問題,但是最終數(shù)據(jù)是一致的(讀多寫少就很合適)。

以上分析借鑒https://blog.csdn.net/qq_35387940/article/details/129611551,非常感謝!

CopyOnWriteArrayList是java.util.concurrent包下面的一個(gè)實(shí)現(xiàn)線程安全的List,顧名思義,CopyOnWriteArrayList在進(jìn)行寫操作(add,remove,set等)時(shí)會進(jìn)行Copy操作,可以推測出在進(jìn)行寫操作時(shí)CopyOnWriteArrayList性能應(yīng)該不會很高。

可以看到CopyOnWriteArrayList底層實(shí)現(xiàn)為Object[] array數(shù)組。

每次添加元素時(shí)都會進(jìn)行Arrays.copyOf操作,代價(jià)非常昂貴。

讀的時(shí)候是不需要加鎖的,直接獲取。刪除和增加是需要加鎖的。

有兩點(diǎn)必須講一下。我認(rèn)為CopyOnWriteArrayList這個(gè)并發(fā)組件,其實(shí)反映的是兩個(gè)十分重要的分布式理念:

(1)讀寫分離

我們讀取CopyOnWriteArrayList的時(shí)候讀取的是CopyOnWriteArrayList中的Object[] array,但是修改的時(shí)候,操作的是一個(gè)新的Object[] array,讀和寫操作的不是同一個(gè)對象,這就是讀寫分離。這種技術(shù)數(shù)據(jù)庫用的非常多,在高并發(fā)下為了緩解數(shù)據(jù)庫的壓力,即使做了緩存也要對數(shù)據(jù)庫做讀寫分離,讀的時(shí)候使用讀庫,寫的時(shí)候使用寫庫,然后讀庫、寫庫之間進(jìn)行一定的同步,這樣就避免同一個(gè)庫上讀、寫的IO操作太多。

(2)最終一致

對CopyOnWriteArrayList來說,線程1讀取集合里面的數(shù)據(jù),未必是最新的數(shù)據(jù)。因?yàn)榫€程2、線程3、線程4四個(gè)線程都修改了CopyOnWriteArrayList里面的數(shù)據(jù),但是線程1拿到的還是最老的那個(gè)Object[] array,新添加進(jìn)去的數(shù)據(jù)并沒有,所以線程1讀取的內(nèi)容未必準(zhǔn)確。不過這些數(shù)據(jù)雖然對于線程1是不一致的,但是對于之后的線程一定是一致的,它們拿到的Object[] array一定是三個(gè)線程都操作完畢之后的Object array[],這就是最終一致。最終一致對于分布式系統(tǒng)也非常重要,它通過容忍一定時(shí)間的數(shù)據(jù)不一致,提升整個(gè)分布式系統(tǒng)的可用性與分區(qū)容錯(cuò)性。當(dāng)然,最終一致并不是任何場景都適用的,像火車站售票這種系統(tǒng)用戶對于數(shù)據(jù)的實(shí)時(shí)性要求非常非常高,就必須做成強(qiáng)一致性的。

性能對比

通過前面的分析可知

Vector對所有操作進(jìn)行了synchronized關(guān)鍵字修飾,性能應(yīng)該比較差

CopyOnWriteArrayList在寫操作時(shí)需要進(jìn)行copy操作,讀性能較好,寫性能較差

Collections.synchronizedList性能較均衡,但是迭代操作并未加鎖,所以需要時(shí)需要額外注意

測試結(jié)果:

ArrayList為什么不是線程安全的,如何保證線程安全?

總結(jié):

  • CopyOnWriteArrayList的寫操作與Vector的遍歷操作性能消耗尤其嚴(yán)重,不推薦使用。

  • CopyOnWriteArrayList適用于讀操作遠(yuǎn)遠(yuǎn)多于寫操作的場景。

  • Vector讀寫性能可以和Collections.synchronizedList比肩,但Collections.synchronizedList不僅可以包裝ArrayList,也可以包裝其他List,擴(kuò)展性和兼容性更好。

三個(gè)方法對比借鑒 原文鏈接:https://blog.csdn.net/lkxsnow/article/details/104130143,非常感謝!文章來源地址http://www.zghlxwxcb.cn/news/detail-468367.html

到了這里,關(guān)于ArrayList為什么不是線程安全的,如何保證線程安全?的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • SimpleDateFormat為什么是線程不安全的?

    SimpleDateFormat為什么是線程不安全的?

    大家好,我是哪吒。 在日常開發(fā)中,Date工具類使用頻率相對較高,大家通常都會這樣寫: 這很簡單啊,有什么爭議嗎? 你應(yīng)該聽過“時(shí)區(qū)”這個(gè)名詞,大家也都知道,相同時(shí)刻不同時(shí)區(qū)的時(shí)間是不一樣的。 因此在使用時(shí)間時(shí),一定要給出時(shí)區(qū)信息。 對于當(dāng)前的上海時(shí)區(qū)和

    2024年02月20日
    瀏覽(22)
  • Java基礎(chǔ):為什么hashmap是線程不安全的?

    HashMap 是線程不安全的主要原因是它的內(nèi)部結(jié)構(gòu)和操作不是線程安全的。下面是一些導(dǎo)致 HashMap 線程不安全的因素: 非同步操作:HashMap 的操作不是線程同步的,也就是說,在多線程環(huán)境下同時(shí)對 HashMap 進(jìn)行讀寫操作可能會導(dǎo)致數(shù)據(jù)不一致的問題。 非原子操作:HashMap 的操作

    2024年02月10日
    瀏覽(20)
  • 再談StringBuilder為什么線程不安全以及帶來的問題

    再談StringBuilder為什么線程不安全以及帶來的問題

    比較有意思的是,學(xué)習(xí)鎖消除的過程中,有人講到StringBuffer在方法內(nèi)構(gòu)建,不會被其他方法引用時(shí),StringBuffer的鎖會被消除, 于是,順便看了一下同源的StringBuidler為什么線程不安全,以及為什么多線程不安全,和帶來的問題, 有了這篇文章,分享出來,幫助讀者輕松應(yīng)對知

    2024年02月11日
    瀏覽(26)
  • 為什么聊天機(jī)器人界面不是未來

    為什么聊天機(jī)器人界面不是未來

    ? 比如: 0 按時(shí)上下班,用固定時(shí)間長度獲取價(jià)值 1 創(chuàng)業(yè),用非線性時(shí)間,獲取真實(shí)價(jià)值 0-1 之間有無限多種狀態(tài) shadow ChatBot目前的交互界面有非常多值得被改進(jìn)的體驗(yàn)機(jī)會。最近看到一篇非常有啟發(fā)性的文章,分享給大家。 核心觀點(diǎn)來自于文章: https://wattenberger.com/though

    2024年02月03日
    瀏覽(20)
  • 為什么字節(jié)大量用GO而不是Java?

    為什么字節(jié)大量用GO而不是Java?

    見字 如面,我是軍哥。 我看很多程序員對字節(jié)編程語言選型很好奇,為此我還特地問了在字節(jié)的兩位4-1的技術(shù)大佬朋友,然后加上自己的思考,總結(jié)了一下就以下 2 個(gè)原因: 1、 選型上沒有歷史包袱 字節(jié)的早期的程序員大多來自于百度、360,本身就是 php / c++ 的背景,一開

    2024年02月08日
    瀏覽(23)
  • 為什么說ChatGPT還不是搜索引擎的對手

    1950年,英國科學(xué)家圖靈在一篇論文中預(yù)言,人類有可能創(chuàng)造出具有真正智能的機(jī)器。 著名的「圖靈測試」就此誕生:如果一臺機(jī)器能夠與人類展開對話,而不被辨別出其機(jī)器身份,那么稱這臺機(jī)器具有智能。 也是從那時(shí)開始,人類世界開始了對人工智能長達(dá)半個(gè)多世紀(jì)的探

    2024年02月11日
    瀏覽(23)
  • 為什么DNS使用UDP而不是TCP詳解!

    DNS(Domain Name System)使用UDP(User Datagram Protocol)而不是TCP(Transmission Control Protocol)的主要原因是出于性能和效率的考慮。下面詳細(xì)解釋為什么DNS選擇使用UDP協(xié)議: 小型請求和快速響應(yīng):DNS查詢通常是小型請求,僅需要幾個(gè)字節(jié)的數(shù)據(jù)傳輸。UDP是無連接的協(xié)議,它不需要在通

    2024年02月02日
    瀏覽(38)
  • 為什么說HTTPS比HTTP安全? HTTPS是如何保證安全的?

    為什么說HTTPS比HTTP安全? HTTPS是如何保證安全的?

    在上篇文章中,我們了解到 HTTP 在通信過程中,存在以下問題: 通信使用明文(不加密),內(nèi)容可能被竊聽 不驗(yàn)證通信方的身份,因此有可能遭遇偽裝 而 HTTPS 的出現(xiàn)正是解決這些問題, HTTPS 是建立在 SSL 之上,其安全性由 SSL 來保證 在采用 SSL 后, HTTP 就擁有了 HTTPS 的加密

    2024年03月19日
    瀏覽(30)
  • 筆記:TCP握手為什么是3次而不是2次?

    這個(gè)問題比較常見,這里簡單總結(jié)一下。 一、兩次握手建立連接:流程說明: 1)客戶端發(fā)送SYN。 2)服務(wù)端收到SYN請求后,服務(wù)端回復(fù)SYN+ACK,然后進(jìn)入已連接狀態(tài)。 3)客戶端收到SYN+ACK回復(fù)后,進(jìn)入已連接狀態(tài)。 二、兩次握手建立連接:存在的問題 若客戶端發(fā)送SYN后,沒

    2023年04月13日
    瀏覽(25)
  • MySQL為什么使用B+樹,而不是B樹?

    在MySQL中,B+樹被廣泛應(yīng)用于索引結(jié)構(gòu),因?yàn)樗С指咝У姆秶樵兒蛥^(qū)間掃描,并且有助于減少磁盤I/O操作,從而提高查詢效率。為什么MySQL使用B+樹而不是B樹?主要有以下幾個(gè)原因: 1、B+樹可以更好地利用磁盤預(yù)讀特性 在數(shù)據(jù)庫中,數(shù)據(jù)通常都存儲在磁盤上。而磁盤的讀

    2024年02月13日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包