ArrayList,Hash..."/>
您好,登錄后才能下訂單哦!
如何理解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)后出。
HashTable
和HashMap
很相似,但HashTable
進(jìn)行了同步處理。
Collections
工具類提供了大量的方法,比如對集合的排序、查找等常用的操作。同時也通過了相關(guān)了方法創(chuàng)建同步容器類
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<>(); 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 < clientTotal; i++) { final Integer j=i; executorService.execute(()->{ 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 < threadTotal; i++) { list.add(i); } for (int i = 0; i < clientTotal; i++) { try{ semaphore.acquire(); executorService.execute(()->{ for (int j = 0; j < list.size(); j++) { list.remove(j); } }); executorService.execute(()->{ for (int j = 0; j < 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(()->{ for (int j = 0; j < list.size(); j++) { list.remove(j); } }); //線程2 executorService.execute(()->{ for (int j = 0; j < 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)境下都是線程安全的。
>線程安全,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<>(); 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 < 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 < 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 < 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<>()); 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 < 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 < 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)知識。
免責(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)容。