JAvaコレクション詳細紹介(二)スレッドセキュリティ

6694 ワード

一、スレッドセキュリティ概念:
  • スレッドセキュリティ:マルチスレッドアクセス時にロックを追加するメカニズムを採用した.つまり、あるスレッドがクラスのデータにアクセスすると、そのデータが保護され、他のスレッドはそのスレッドが読み取り終わるまでアクセスできません.データが一致しなかったり、データが汚染されたりすることを防止します.
  • スレッドは安全ではありません:データアクセスを提供しない時のデータ保護で、複数のスレッドは同時にあるデータを操作することができて、それによってデータの不一致あるいはデータの汚染の情況が現れます.
  • スレッドが安全でないという問題では、synchronizedキーワードロック同期制御が一般的に使用されます.
  • スレッドセキュリティ動作原理:jvmにはmain memoryオブジェクトがあり、各スレッドにも独自のworking memoryがあり、1つのスレッドが1つの変数variableに対して操作を行う場合は、自分のworking memoryにcopyを作成し、操作が終わったらmain memoryに書き込む必要があります.  複数のスレッドが同じ変数variableを操作すると、予知できない結果が発生する可能性があります.  synchronizedを使用する鍵はモニタを確立することであり、このモニタは修正する変数であってもよいし、他の自分が適切だと思っているオブジェクトであってもよい(方法)、その後、このモニタにロックをかけることでスレッドセキュリティを実現し、各スレッドはこのロックを取得した後、loadをworking memoryからuse&&にロードし、assignをストレージstoreに割り当て、main memoryに割り当てるプロセスを実行します.ロックが解放されますこれにより,いわゆるスレッドセキュリティが実現される.

  • スレッドセキュリティ(Thread-safe)の集合オブジェクト: Vector 、HashTable.
    非スレッドセキュリティの集合オブジェクト:ArrayList、LinkedList、HashMap、HashSet、TreeMap、TreeSet.
    二、常用集中集合で遭遇するスレッドセキュリティ問題とその解決方法
    (一)List
          1.1マルチスレッド環境のシミュレーション
              マルチスレッド環境ではjava.util.C o n c u r r r e ntModificationException異常が放出されます
     
       public static void listNotSafe() {
           List list = new CopyOnWriteArrayList<>();
       
           for (int i = 0; i < 30; i++) {
               new Thread(() -> {
                   list.add(UUID.randomUUID().toString().substring(0, 8));
                   System.out.println(list);
               }).start();
           }
       }

     
     
          1.2異常原因
                 ,             。

          1.3解決方法
     
       // 1.         Vector
       new Vector();
       
       // 2.    Collections       ArrayList
       Collections.synchronizedList(new ArrayList<>());
       
       // 3.    java.util.concurrent.CopyOnWriteArrayList;
       new CopyOnWriteArrayList<>();
    

     
           1.4書く時に思想をコピーする
           CopyOnWrite           。            ,        Object[]  ,        Object[]  Copy,          Object[] newElements,       Object[] newElements      ,       ,               setArray(newElements);           CopyOnWrite        ,      ,              。  CopyOnWrite             ,        。

     
       // CopyOnWriteArrayList.java
       public boolean add(E e) {
           final ReentrantLock lock = this.lock;
           lock.lock();
           try {
               Object[] elements = getArray();
               int len = elements.length;
               Object[] newElements = Arrays.copyOf(elements, len + 1);
               newElements[len] = e;
               setArray(newElements);
               return true;
           } finally {
              lock.unlock();
           }
       }

     
      (二)Set
           2.1スレッドセキュリティの問題
               Listインタフェースのテスト方法と同様にjava.util.C o n c u r r e ntModificationException異常が放出されます.
           2.2解決方法
           // 1.    Collections      
           Collections.synchronizedSet(new HashSet<>());
       
           // 2.    java.util.concurrent.CopyOnWriteArraySet;
           new CopyOnWriteArraySet<>();

     
           2.3 CopyOnWriteArraySet
     
           //          CopyOnWriteArrayList
           public class CopyOnWriteArraySet extends AbstractSet
                   implements java.io.Serializable {
               private static final long serialVersionUID = 5457747651344034263L;
           
               private final CopyOnWriteArrayList al;
           
               // ...
           }

     
     
          //     ,      CopyOnWriteArrayList   addIfAbsent()   
           public class CopyOnWriteArraySet {
               public boolean add(E e) {
                       return al.addIfAbsent(e);
               }
           }
       
           /**
            * CopyOnWriteArrayList   addIfAbsent()   
            * Set           ,             ,      false
            *   ,         
            */
           public boolean addIfAbsent(E e) {
               Object[] snapshot = getArray();
               return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
               addIfAbsent(e, snapshot);
           }
       
           /**
            *     addIfAbsent()   ,           ,      ,          ,    
            *         ,             ,               ,    
            *  ,     false,       ,         。
            */
           private boolean addIfAbsent(E e, Object[] snapshot) {
               final ReentrantLock lock = this.lock;
               lock.lock();
               try {
                   Object[] current = getArray();
                   int len = current.length;
                   if (snapshot != current) {
                       // Optimize for lost race to another addXXX operation
                       int common = Math.min(snapshot.length, len);
                       for (int i = 0; i < common; i++)
                           if (current[i] != snapshot[i] && eq(e, current[i]))
                               return false;
                       if (indexOf(e, current, common, len) >= 0)
                           return false;
                   }
                   Object[] newElements = Arrays.copyOf(current, len + 1);
                   newElements[len] = e;
                   setArray(newElements);
                   return true;
               } finally {
                   lock.unlock();
               }
           }

     
     (三)Map
             3.1スレッドセキュリティの問題
                       上記と同様に、マルチスレッド環境ではjava.util.C o n c u r r e ntModificationException異常が放出されます.
             3.2解決方法
       //    Collections    
       Collections.synchronizedMap(new HashMap<>());
       
       //    ConcurrentHashMap
       new ConcurrentHashMap<>();

     
    3.3 HashMap、HashtableとConcurrentHashMapの違い
    継承の違い:HashMapはAbstractMapを継承し、HashtableはDictonaryを継承し、ConcurrentHashMapはAbstractMapを継承するほか、ConcurrentMapインタフェースも実現した
    スレッドが安全かどうか:HashMap非スレッドセキュリティ、ConcurrentHashMapおよびHashtableスレッドセキュリティ.しかし、それらの実装メカニズムは異なり、Hashtabl eはsynchronizedを使用して同期方法を実装し、ConcurrentHashMapはロックの粒度を低下させ、より良い同時性能を有する.
    KeyValue値:ConcurrentHashMapとHashtableは、valueとkeyがnullであることを許可しませんが、HashMapは唯一のkeyがnullであることを許可し、任意のvalueがnullであることを許可します.
    ハッシュアルゴリズムの違い:HashMapとJdk 8のConcurrentHashMapのアルゴリズムは一致してkeyのhashcode値を用いて高16ビットと低16ビットの異型または再型長を行い、Hashtableはkeyのhashcode値を直接型取り操作する.
    拡張機構は異なる:ConcurrentHashMapとHashMapの拡張機構は初期容量と一致し,拡張は元の配列長の2倍,初期容量は16であったが,hashtableの初期容量は11であり,容量は元の長さの2倍+1であった.
    失敗メカニズム:ConcurrentHashMapサポートセキュリティ失敗、HashMapとhashtableサポートの迅速な失敗
    クエリー・メソッド:HashMapにはcontainsメソッドはありませんが、containsKeyメソッドとcontainsValueメソッドがあり、HashtableメソッドとConcurrentHashMapではcontainsメソッドもサポートされています.
    反復方式:ConcurrentHashMapとHashtableはEnumeration反復方式もサポートする