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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java高并发(十一)同步容器

發布時間:2024/9/16 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java高并发(十一)同步容器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

上面一節我們介紹了ArrayList、HashSet、HashMap這些容器都是非線程安全的。如果有多個線程并發訪問這些容器時,就會觸發線程安全問題。因此在編寫程序的時候,必須要求開發人員手動的在任何訪問到這些容器的地方進行同步處理,這樣就導致使用起來非常不便。因此java提供了同步容器方便使用。

在java中同步容器主要包括兩類:

  • ArrayList -> Vector,Stack;? ? ?HashMap -> HashTable(key,value不能為null)
  • Collections.synchronizedXXX(List, Set, Map)

?Vector

Vector實現了List接口,實際上就是一個數組。與ArrayList非常類似。但是Vector中的所有方法都是使用synchronized方法修飾的方法,進行了同步的措施。因此在多線程環境下使用ArrayList對象時,如果被多個線程共享使用可以換成同步的Vector,這樣的話線程安全型會更好一些(而不是完全線程安全的)。

@Slf4j @ThreadSafe public class VectorExample1 {// 請求總數public static int clientTotal = 5000;// 同時并發執行的線程數public static int threadTotal = 200;private static List<Integer> list = new Vector<>();public static void main(String[] args) throws InterruptedException {//線程池ExecutorService executorService = Executors.newCachedThreadPool();//定義信號量final Semaphore semaphore = new Semaphore(threadTotal);//定義計數器final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);for(int i = 0; i < clientTotal; i++) {final int count = i;executorService.execute(() ->{try {semaphore.acquire();update(count);semaphore.release();} catch (InterruptedException e) {log.error("exception", e);}countDownLatch.countDown();});}countDownLatch.await();executorService.shutdown();log.info("size:{}",list.size()) ;}public static void update(int i) {list.add(i);}}

這樣輸出的結果就是預期的結果。

為什么說同步容器不是線程安全的?

@NotThreadSafe public class VectorExample2 {private static Vector<Integer> vector = new Vector<>();public static void main(String[] args) {while (true){for (int i = 0;i < 10;i++) {vector.add(i);}Thread thread1 = new Thread(){@Overridepublic void run() {for (int i = 0;i < vector.size();i++) {vector.remove(i);}}};Thread thread2 = new Thread(){@Overridepublic void run() {for (int i = 0;i < vector.size();i++) {vector.get(i);}}};thread1.start();thread2.start();}} }

運行,拋出異常:

Exception in thread "Thread-611" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 5at java.util.Vector.get(Vector.java:748)at com.vincent.example.syncContainer.VectorExample2$2.run(VectorExample2.java:25) Exception in thread "Thread-1759" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 21at java.util.Vector.get(Vector.java:748)at com.vincent.example.syncContainer.VectorExample2$2.run(VectorExample2.java:25)

原因:get發生越界肯定是remove方法引起的,vector雖然能保證同一時刻只能有一個線程能訪問他,但是不排除有這種可能:當某個線程某個時刻執行到int i = 0;i < vector.size()時,vector.size()返回10,i=9;而另外一個線程正好將i=9的vector移除掉了,這時get方法想調用i=9的元素就會出現數組越界的異常。

這個例子演示了兩個同步容器的兩個同步方法因為操作順序的差異,在不同線程里面可能會觸發線程不安全的問題。因此為了保證線程安全,必須在方法調用端做一些額外的同步措施才可以。在使用同步容器時并不是在所有場合下都是線程安全的。

Stack

Stack中的方法也使用了synchronized修飾了,實際上Stack類繼承了Vector類。

HashTable

HashTable實現了Map接口,與HashMap很相似,但是HashTable進行了同步處理,方法也是使用了synchronized進行了修飾。但是在使用HashTable時一定要注意key和value是不能為null的。

Collections

將新建ArrayList、HashSet、HashMap對象由Collections產生:

private static List<Integer> list = Collections.synchronizedList(new ArrayList<>()); private static Set<Integer> set = Collections.synchronizedSet(new HashSet<>()); private static Map<Integer, Integer> map = Collections.synchronizedMap(new HashMap<>());

在集合遍歷過程中刪除操作

public class VectorExample3 {//Exception in thread "main" java.util.ConcurrentModificationExceptionprivate static void test1(Vector<Integer> v1){ //foreachfor(Integer i: v1){if(i.equals(3)){v1.remove(i);}}}//Exception in thread "main" java.util.ConcurrentModificationExceptionprivate static void test2(Vector<Integer> v1){ //iteratorIterator<Integer> integerIterator = v1.iterator();while (integerIterator.hasNext()) {Integer i = integerIterator.next();if(i.equals(3)){v1.remove(i);}}}// successprivate static void test3(Vector<Integer> v1){for(int i = 0; i < v1.size(); i++) {if(v1.equals(3)){v1.remove(i);}}}public static void main(String[] args) {Vector<Integer> vector = new Vector<>();vector.add(1);vector.add(2);vector.add(3);test1(vector);} }

如果使用了Foreach或者迭代器來循環我們的集合時,盡量不要在循環中做集合的刪除操作,如果要做remove操作時,建議在遍歷的過程中發現需要刪除的值然后做一個標記,在遍歷結束后在執行相應的remove操作。在多線程情況下出現異常的情況會更大。

一般情況下我們通常使用并發容器來代替同步容器

總結

以上是生活随笔為你收集整理的java高并发(十一)同步容器的全部內容,希望文章能夠幫你解決所遇到的問題。

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