日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java之List系列--ArrayList保证线程安全的方法

發布時間:2024/1/8 java 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java之List系列--ArrayList保证线程安全的方法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文網址:Java之List系列--ArrayList保證線程安全的方法_IT利刃出鞘的博客-CSDN博客

簡介

本文介紹Java中的ArrayList、LinkedList如何進行線程安全的操作、為什么ArrayList不是線程安全的。

這幾個問題也是Java后端面試中經常問到的問題。

線程安全的操作方法

ArrayList

方法

示例

原理

Vector

List list = new ArrayList();

替換為List arrayList = new Vector<>();

使用了synchronized關鍵字

Collections

.synchronizedList(list)

List<String> list = Collections

? ? ? ? .synchronizedList(new ArrayList<String>());

操作外部list,實際上修改的是原來list的數據。

所有方法都加了synchronized修飾。加鎖的對象是當前SynchronizedCollection實例。

JUC中的

CopyOnWriteArrayList

CopyOnWriteArrayList<String> list =

? ? ? ? new CopyOnWriteArrayList<String>();

適用于讀多寫少的并發場景。

Write的時候總是要Copy(將原來array復制到新的array,修改后,將引用指向新數組)。任何可變的操作(add、set、remove等)都通過ReentrantLock 控制并發。

LinkedList

方法示例原理
Collections.synchronizedList(List)public static List linkedList = Collections.synchronizedList(new LinkedList());所有方法都加了synchronized修飾。加鎖的對象是當前SynchronizedCollection實例。
JUC中的ConcurrentLinkedQueueConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue();

線程不安全問題復現

實例

package org.example.a;import java.util.ArrayList; import java.util.List;class MyThread extends Thread{public void run(){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}Demo.arrayList.add(Thread.currentThread().getName() + " " + System.currentTimeMillis());} }public class Demo{public static List arrayList = new ArrayList();public static void main(String[] args) {Thread[] threadArray = new Thread[1000];for(int i = 0;i < threadArray.length;i++){threadArray[i] = new MyThread();threadArray[i].start();}for(int i = 0;i < threadArray.length;i++){try {threadArray[i].join();} catch (InterruptedException e) {e.printStackTrace();}}for(int i = 0;i < arrayList.size(); i++){System.out.println(arrayList.get(i));}} }

運行結果

Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 49at java.util.ArrayList.add(ArrayList.java:459)at org.example.a.MyThread.run(Demo.java:13) Thread-3 1590288167830 Thread-7 1590288167834 Thread-57 1590288167834 ... null Thread-951 1590288168255 Thread-254 1590288168255 ...

總共有四種情況:

  • 正常輸出
  • 輸出值為null;
  • 數組越界異常;
  • 某些線程沒有輸出值;
  • 線程不安全的原因分析

    ArrayList源碼

    public boolean add(E e) {// 確保ArrayList的長度足夠ensureCapacityInternal(size + 1); // Increments modCount!!// ArrayList加入elementData[size++] = e;return true; }private void ensureCapacityInternal(int minCapacity) {if (elementData == EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}ensureExplicitCapacity(minCapacity); }private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity); }// 如果超過界限 數組長度增長 private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity); }

    在上述過程中,會出問題的地方是: 1. 增加元素 2. 擴充數組長度;

    情景1:增加元素

    ? ? ? ? 增加元素過程中較為容易出現問題的地方是elementData[size++] = e;。賦值的過程可以分為兩個步驟elementData[size] = e; size++;。

    例如size為1,有兩個線程,分別加入字符串“a”與字符串“b”:

    如果四條語句按照:1,2,3,4執行,那么沒有問題。

    如果按照1,3,2,4來執行,就會出錯。以下步驟按時間先后排序:

  • 線程1 賦值 element[1] = "a"; 隨后因為時間片用完而中斷;
  • 線程2 賦值 element[1] = "b; 隨后因為時間片用完而中斷;
    ? ? 此處導致了之前所說的一個問題(有的線程沒有輸出); 因為后續的線程將前面的線程的值覆蓋了。
  • 線程1 自增 size++; (size=2)
  • 線程2 自增 size++; (size=3)
    ? ? 此處導致了某些值為null的問題。因為原來size=1, 但是因為線程1與線程2都將值賦值給了element[1],導致了element[2]內沒有值,被跳過了。此時指針index指向了3,所以導致了值為null的情況。
  • 情景2:數組越界

    例如:size為2,數組長度限制為2,有兩個線程,分別加入字符串“a”與字符串“b”:

    ??如果四條語句按照:1,2,3,4,5,6執行,那么沒有問題。

    前提條件: 當前size=2 數組長度限制為2。

    如果按照1,3,2,4來執行,就會出錯。以下步驟按時間先后排序:

  • 語句1:線程1 判斷數組是否越界。因為size=2 長度為2,沒有越界,將進行賦值操作。但是因為時間片用完而中斷。
  • 語句4:線程2 判斷數組是否越界。因為size=2 長度為2,沒有越界,將進行賦值操作。但是因為時間片用完而中斷。
  • 語句2,3:線程1重新獲取到時間片,上文判斷了數組不會越界,所以進行賦值操作element[size]=“a”,并且size++
  • 語句5,6:線程2重新獲取到時間片,上文判斷了數組不會越界,所以進行賦值操作。但是此時的size=3了,再執行element[3]="b"導致了數組越界。
  • 由此處可以看出因為數組的當前指向size并未進行加鎖的操作,導致了數組越界的情況出現。

    其他網址

    ArrayList 線程安全問題_Java_Be yourself.-CSDN博客

    LinkedList的線程安全處理_筱白to的博客-CSDN博客_線程安全的linkedlist

    總結

    以上是生活随笔為你收集整理的Java之List系列--ArrayList保证线程安全的方法的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。