ArrayList詳解(線程不安全,源碼分析)

ArrayList線程不安全

不安全事例代碼

 public static void main(String[] args) {
        final ArrayList<Integer> arrayList = new ArrayList<>();
        for(int i=0;i<10000;i++){
            final int a = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    arrayList.add(a);
                    System.out.println(a);
                }
            }).start();
        }
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for(int i=0;i<arrayList.size();i++){
            System.out.println(arrayList.get(i));
        }
        System.out.println("================ size = " + arrayList.size());
    }

????結(jié)果:

1564621300(1).jpg

發(fā)現(xiàn)缺少了兩條數(shù)據(jù),插入數(shù)據(jù)總個(gè)數(shù)10000,結(jié)果遍歷只顯示有9998條數(shù)據(jù)

不安全原因

1564574479(1).jpg

????查看源代碼可以了解到ArrayList插入數(shù)據(jù)的方法分兩步,第一步是擴(kuò)容集合長度,第二步放入數(shù)據(jù)。當(dāng)我們多個(gè)線程同時(shí)進(jìn)行插入數(shù)據(jù)的操作時(shí),某個(gè)線程執(zhí)行了第一步,擴(kuò)大了集合大小,同時(shí)另一個(gè)線程正好也執(zhí)行了這一步,又?jǐn)U大了一級(jí),此時(shí)會(huì)產(chǎn)生新的數(shù)組對(duì)象,最后兩個(gè)同時(shí)執(zhí)行賦值的時(shí)候,賦值位置正好都在size上,那么此時(shí)會(huì)發(fā)現(xiàn)在一個(gè)位置,而兩次擴(kuò)容生成的對(duì)象不同,所以后執(zhí)行賦值的會(huì)把之前的覆蓋掉。

解決辦法

  • List<String> list = Collections.synchronizedList(new ArrayList<>());
  • 使用其他安全的來代替

ArrayList源碼分析

????ArrayList實(shí)際上就是對(duì)數(shù)組進(jìn)行不斷的擴(kuò)容,初始默認(rèn)長度為10。

1564575992(1).jpg

????每次執(zhí)行add方法,相當(dāng)于為數(shù)組對(duì)應(yīng)位置賦值,有一個(gè)全局變量SIZE,初始化為0,每次賦值都為size位置上賦值,之后size加1。

????ArrayList實(shí)例化的時(shí)候允許我們傳入初始化數(shù)組大小,默認(rèn)是10,。所以當(dāng)我們知道要插入數(shù)據(jù)的總個(gè)數(shù)時(shí)候,可以在初始化的時(shí)候直接定義list的大小,這樣可以防止每次插入數(shù)據(jù)的時(shí)候總會(huì)復(fù)制數(shù)組浪費(fèi)資源。

1564623587(1).jpg

????執(zhí)行add方法時(shí)候,分兩步,第一步判斷是否數(shù)組需要擴(kuò)容第二步插入數(shù)據(jù)。其中第一步會(huì)執(zhí)行到grow方法,在該方法中進(jìn)行數(shù)組的擴(kuò)容。

1564623822(1).jpg

????在執(zhí)行g(shù)row進(jìn)行數(shù)組擴(kuò)容之前,還會(huì)先經(jīng)歷三個(gè)ensureCapacityInternal 來確定是否需要擴(kuò)容,以及擴(kuò)容多少,按照現(xiàn)在它的算法,每次擴(kuò)容會(huì)擴(kuò)原數(shù)組一半大小,即原長度為9,則擴(kuò)容后為9+4=13。

1564623993(1).jpg
  • arraylist 是可以查找第一個(gè)null的位置的,請(qǐng)看源碼
1564622508(1).jpg
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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