ArrayList,Hash..."/>
溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何理解Java同步容器

發(fā)布時間:2021-11-20 14:55:20 來源:億速云 閱讀:128 作者:柒染 欄目:云計算

如何理解Java同步容器,針對這個問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

> ArrayList,HashSet,HashMap都是線程非安全的,在多線程環(huán)境下,會導(dǎo)致線程安全問題,所以在使用的時候需要進(jìn)行同步,這無疑增加了程序開發(fā)的難度。所以JAVA提供了同步容器。

同步容器

  • ArrayList ===> Vector,Stack

  • HashMap ===> HashTable(key,value都不能為空)

  • Collections.synchronizedXXX(List,Set,Map)

Vector實現(xiàn)List接口,底層和ArrayList類似,但是Vector中的方法都是使用synchronized修飾,即進(jìn)行了同步的措施。 但是,Vector并不是線程安全的。

Stack也是一個同步容器,也是使用synchronized進(jìn)行同步,繼承與Vector,是數(shù)據(jù)結(jié)構(gòu)中的,先進(jìn)后出。

HashTableHashMap很相似,但HashTable進(jìn)行了同步處理。

Collections工具類提供了大量的方法,比如對集合的排序、查找等常用的操作。同時也通過了相關(guān)了方法創(chuàng)建同步容器類

Vector

package com.rumenz.task;

import java.util.List;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

//線程安全
public class VectorExample1 {
    public static Integer clientTotal=5000;
    public static Integer thradTotal=200;
    private static List<integer> list=new Vector&lt;&gt;();

    public static void main(String[] args) throws Exception{

        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore=new Semaphore(thradTotal);
        final CountDownLatch countDownLatch=new CountDownLatch(clientTotal);
        for (int i = 0; i &lt; clientTotal; i++) {
            final Integer j=i;
            executorService.execute(()-&gt;{
                try {
                    semaphore.acquire();
                    update(j);
                    semaphore.release();

                }catch (Exception e){
                    e.printStackTrace();
                }
                countDownLatch.countDown();
            });
            
        }
        countDownLatch.await();
        executorService.shutdown();
        System.out.println("size:"+list.size());

    }

    private static void update(Integer j) {
        list.add(j);
    }
}

同步容器不一定就線程安全

package com.rumenz.task;

import scala.collection.convert.impl.VectorStepperBase;

import java.util.List;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

//線程不安全
public class VectorExample2 {
    public static Integer clientTotal=5000;
    public static Integer threadTotal=200;

    private static List<integer> list=new Vector();
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore=new Semaphore(threadTotal);
        for (int i = 0; i &lt; threadTotal; i++) {
            list.add(i);
        }
        for (int i = 0; i &lt; clientTotal; i++) {
            try{
                semaphore.acquire();
                executorService.execute(()-&gt;{
                    for (int j = 0; j &lt; list.size(); j++) {
                        list.remove(j);
                    }
                });
                executorService.execute(()-&gt;{
                    for (int j = 0; j &lt; list.size(); j++) {
                        list.get(j);
                    }
                });
                semaphore.release();

            }catch (Exception e){
                e.printStackTrace();
            }

            
        }
        executorService.shutdown();
    }

}

運行報錯

Exception in thread "pool-1-thread-2" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 36
	at java.util.Vector.get(Vector.java:751)
	at com.rumenz.task.VectorExample2.lambda$main$1(VectorExample2.java:38)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

原因分析

> Vector是線程同步容器,size(),get(),remove()都是被synchronized修飾的,為什么會有線程安全問題呢?

>get()拋出的異常肯定是remove()引起的,Vector能同時保證同一時刻只有一個線程進(jìn)入,但是:

//線程1
 executorService.execute(()-&gt;{

  for (int j = 0; j &lt; list.size(); j++) {
    list.remove(j);
  }
});
//線程2
executorService.execute(()-&gt;{
  for (int j = 0; j &lt; list.size(); j++) {
    list.get(j);
  }
});
  • 線程1和線程2都執(zhí)行完list.size(),都等于200,并且j=100

  • 線程1執(zhí)行list.remove(100)操作,

  • 線程2執(zhí)行list.get(100)就會拋出數(shù)組越界的異常。

> 同步容器雖然是線程安全的,但是不代表在任何環(huán)境下都是線程安全的。

HashTable

>線程安全,key,value都不能為null。在修改數(shù)據(jù)時鎖住整個HashTable,效率低下。初始size=11。

package com.rumenz.task;

import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

//線程安全
public class HashTableExample1 {

    public static Integer clientTotal=5000;
    public static Integer threadTotal=200;
    private static Map<integer,integer> map=new Hashtable&lt;&gt;();

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore=new Semaphore(threadTotal);
        final CountDownLatch countDownLatch=new CountDownLatch(clientTotal);
        for (int i = 0; i &lt; clientTotal; i++) {
            final Integer j=i;
            try{
               semaphore.acquire();
               update(j);
               semaphore.release();
            }catch (Exception e){
                e.printStackTrace();
            }
            countDownLatch.countDown();
            
        }
        countDownLatch.await();

        executorService.shutdown();
        System.out.println("size:"+map.size());


    }

    private static void update(Integer j) {
        map.put(j, j);
    }
}

//size:5000

Collections.synchronizedList線程安全

package com.rumenz.task.Collections;

import com.google.common.collect.Lists;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
//線程安全
public class synchronizedExample {
    public static Integer clientTotal=5000;
    public static Integer threadTotal=200;

    private static List<integer> list=Collections.synchronizedList(Lists.newArrayList());

    public static void main(String[] args) throws Exception{
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore=new Semaphore(threadTotal);
        final CountDownLatch countDownLatch=new CountDownLatch(clientTotal);
        for (int i = 0; i &lt; clientTotal; i++) {
            final Integer j=i;
             try{
                 semaphore.acquire();
                 update(j);
                 semaphore.release();
             }catch (Exception e){
                 e.printStackTrace();
             }
             countDownLatch.countDown();
        }
        countDownLatch.await();
        executorService.shutdown();
        System.out.println("size:"+list.size());

    }

    private static void update(Integer j) {
        list.add(j);
    }
}
//size:5000

Collections.synchronizedSet線程安全

package com.rumenz.task.Collections;

import com.google.common.collect.Lists;
import org.assertj.core.util.Sets;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

//線程安全
public class synchronizedSetExample {

    public static Integer clientTotal=5000;
    public static Integer threadTotal=200;

    private static Set<integer> set=Collections.synchronizedSet(Sets.newHashSet());

    public static void main(String[] args) throws Exception{
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore=new Semaphore(threadTotal);
        final CountDownLatch countDownLatch=new CountDownLatch(clientTotal);
        for (int i = 0; i &lt; clientTotal; i++) {
            final Integer j=i;
             try{
                 semaphore.acquire();
                 update(j);
                 semaphore.release();
             }catch (Exception e){
                 e.printStackTrace();
             }
             countDownLatch.countDown();
        }
        countDownLatch.await();
        executorService.shutdown();
        System.out.println("size:"+set.size());

    }

    private static void update(Integer j) {
        set.add(j);
    }
}
//size:5000

Collections.synchronizedMap線程安全

package com.rumenz.task.Collections;

import org.assertj.core.util.Sets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class synchronizedMapExample {

    public static Integer clientTotal=5000;
    public static Integer threadTotal=200;

    private static Map<integer,integer> map=Collections.synchronizedMap(new HashMap&lt;&gt;());

    public static void main(String[] args) throws Exception{
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore=new Semaphore(threadTotal);
        final CountDownLatch countDownLatch=new CountDownLatch(clientTotal);
        for (int i = 0; i &lt; clientTotal; i++) {
            final Integer j=i;
            try{
                semaphore.acquire();
                update(j);
                semaphore.release();
            }catch (Exception e){
                e.printStackTrace();
            }
            countDownLatch.countDown();
        }
        countDownLatch.await();
        executorService.shutdown();
        System.out.println("size:"+map.size());

    }

    private static void update(Integer j) {
        map.put(j, j);
    }
}
//size:5000

> Collections.synchronizedXXX在迭代的時候,需要開發(fā)者自己加上線程鎖控制代碼,因為在整個迭代過程中循環(huán)外面不加同步代碼,在一次次迭代之間,其他線程對于這個容器的add或者remove會影響整個迭代的預(yù)期效果,這個時候需要在循環(huán)外面加上synchronized(XXX)。

集合的刪除

  • 如果在使用foreach或iterator進(jìn)集合的遍歷,

  • 盡量不要在操作的過程中進(jìn)行remove等相關(guān)的更新操作。

  • 如果非要進(jìn)行操作,則可以在遍歷的過程中記錄需要操作元素的序號,

  • 待遍歷結(jié)束后方可進(jìn)行操作,讓這兩個動作分開進(jìn)行

package com.rumenz.task;

import com.google.common.collect.Lists;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;


public class CollectionsExample {

    private static List<integer> list=Collections.synchronizedList(Lists.newArrayList());

    public static void main(String[] args) {
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        //del1();
        //del2();
        del3();

    }

    private static void del3() {
        for(Integer i:list){

            if(i==4){
                list.remove(i);
            }
        }

    }
    //Exception in thread "main" java.util.ConcurrentModificationException
    private static void del2() {
        Iterator<integer> iterator = list.iterator();
        while (iterator.hasNext()){
            Integer i = iterator.next();
            if(i==4){
                list.remove(i);
            }
        }

    }
    //Exception in thread "main" java.util.ConcurrentModificationException
    private static void del1() {
        for (int i = 0; i &lt; list.size(); i++) {
            if(list.get(i)==4){
                list.remove(i);
            }
        }

    }
}

> 在單線程會出現(xiàn)以上錯誤,在多線程情況下,并且集合時共享的,出現(xiàn)異常的概率會更大,需要特別的注意。解決方案是希望在foreach或iterator時,對要操作的元素進(jìn)行標(biāo)記,待循環(huán)結(jié)束之后,在執(zhí)行相關(guān)操作。

> 同步容器采用synchronized進(jìn)行同步,因此執(zhí)行的性能會受到影響,并且同步容器也并不一定會做到線程安全。

關(guān)于如何理解Java同步容器問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI