スレッドセキュリティとセキュリティポリシー

6139 ワード

スレッドのセキュリティ
複数のスレッドがクラスにアクセスすると、実行環境がどのようなスケジューリング方式を採用しているか、またはこれらのスレッドがどのように交互に実行されるかにかかわらず、プライマリ・コール・コードに追加の同期や協同を必要とせず、このクラスは正しい動作を示すことができ、このクラスはスレッド・セキュリティです.
  • 原子性:反発アクセスが提供され、同じ時点で
  • を操作するスレッドは1つしかありません.
  • 可視性:1つのスレッドによるプライマリメモリの変更は、
  • を他のスレッドによってタイムリーに観察することができる.
  • 秩序性:1つのスレッドは他のスレッドにおける命令実行順序を観察し、命令再ソートの存在により、この観察結果は一般的に乱雑である
  • である.
    げんしせい
    原子間性:相互反発アクセスが提供され、同じ時点で1つのスレッドしか操作できません.
  • atomicパッケージ、Atomicで始まるクラス、Javaの下位層はCASを使用して実装される.例えば、AtomicIntegerはjavaの下位層のUnsafeを呼び出す.compareAndSwapIntメソッド、ここではCASを実装する鍵であり、Unsafeクラスで呼び出されるメソッドの多くはnativeであり、jvmによって
  • がローカルに実装される.
  • AtomicLong、LongAdder
  • AtomicReference AtomicReferenceFieldUpdater
  • @Slf4j
    @ThreadSafe
    public class AtomicExample4 {
    
        private static AtomicReference count = new AtomicReference<>(0);
    
        public static void main(String[] args) {
            count.compareAndSet(0,2); // 2
            count.compareAndSet(0,1); // no
            count.compareAndSet(1,3); // no
            count.compareAndSet(2,4); // 4
            count.compareAndSet(3,5); // no
            log.info("count:{}",count.get());
        }
    }
    
    @Slf4j
    public class Test3 {
    
        private static AtomicReferenceFieldUpdater updater = 
        AtomicReferenceFieldUpdater.newUpdater(Test3.class, Integer.class, "count");
    
        private volatile Integer count = 0; //         volatile      
    
        public static void main(String[] args) throws Exception{
            Test3 test3 = new Test3();
            updater.compareAndSet(test3, 0, 1);
            updater.compareAndSet(test3, 0, 2);
            updater.compareAndSet(test3, 1, 3);
            updater.compareAndSet(test3, 2, 4);
            log.info("  :{}", updater.get(test3));
    
        }
    
    }
    
  • AtomicStampReference:CASのABA問題このクラスのコアメソッドcompareAndSetは、stampの値を再定義することによって現在の値が変更されたかどうかをマークする.
  • AtomicBoolean
  • @Slf4j
    @ThreadSafe
    public class Test5 {
        //     
        private static int clientTotal = 5000;
        //          
        private static int threadNum = 200;
        private static AtomicBoolean isHappen = new AtomicBoolean(false);
    
        public static void main(String[] args) throws Exception {
            final Semaphore semaphore = new Semaphore(threadNum);
            final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
            ExecutorService executorService = Executors.newCachedThreadPool();
    
            for (int i = 0; i < clientTotal; i++) {
                executorService.execute(() -> {
                    try {
                        semaphore.acquire();
                        update();
                        semaphore.release();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    countDownLatch.countDown();
                });
    
            }
            countDownLatch.await();
            executorService.shutdown();
            log.info("ishappen:{}", isHappen.get());
        }
    
        private static void update() {
            if (isHappen.compareAndSet(false, true)) { //      
                System.out.println("update exec");
            }
        }
    }
    

    可視性
    可視性:1つのスレッドによるプライマリメモリの変更は、他のスレッドによってタイムリーに観察されます.
    JMM synchronizedに関する2つの規定:
  • スレッドがロック解除される前に、共有変数の最新値をメインメモリ
  • にリフレッシュする必要があります.
  • スレッドのロック時には、ワークメモリの共有変数の値をクリアし、共有変数ではなくメインメモリから最新の値を読み直す必要がある(ロックとロック解除は同じロックであることに注意)
  • .
    synchronizedが可視性に影響する問題について
    synchronized
  • 修飾コードブロック:カッコで囲まれたコードで、呼び出されたオブジェクト
  • に作用する.
  • 修飾メソッド:呼び出しオブジェクト
  • に作用するメソッド全体
  • 静的方法を修飾する:静的方法全体、すべてのオブジェクトに作用する
  • 修飾クラス、括弧で囲まれた部分、すべてのオブジェクトに作用する
  • synchronized:割り込み不可ロック、競争が激しくなく、可読性が良いロック:割り込み可能ロック、多様化同期、競争が激しい場合は常態Atomicを維持できる:競争が激しい場合は常態を維持でき、ロックより性能がよく、1つの値しか同期できない
    volatile
    メモリバリアの追加と再トレーニングの禁止による最適化:
  • volatile変数の書き込み操作の場合、書き込み操作後にstoreバリアコマンドが追加され、ローカルメモリの共有変数値がプライマリメモリにリフレッシュされます.
  • volatile変数の読み出し操作の場合、読み出し操作の前にloadバリア指令が加わる、共有変数
  • がホストメモリから読み出される.
    秩序性
    Javaメモリモデルでは、コンパイラとプロセッサが命令を並べ替えることができますが、並べ替えプロセスは単一スレッドプログラムの実行には影響しませんが、マルチスレッドの同時実行の正確性に影響します.
    秩序性:1つのスレッドは他のスレッドの命令実行順序を観察し、命令再ソートの存在により、この観察結果は一般的に乱雑である.
    2つの操作の実行順序がhappens-beforeの原則から導き出せない場合、彼らの秩序性を保証することはできず、jvmは勝手に彼らを並べ替えることができる.
    happen-before原則:
  • プログラム順序規則:1つの個別のスレッドにおいて、プログラムコードの実行フロー順序に従って、(時間的に)先に実行された操作happen-before(時間的に)後に実行される操作.
  • ロックルールの管理:unlock操作happen-beforeの後(時間的な前後順序、以下同じ)同じロックに対するlock操作.
  • volatile変数規則:1つのvolatile変数に対する書き込み操作happen-beforeの後にある変数の読み取り操作.
  • スレッド起動規則:Threadオブジェクトのstart()メソッドhappen-beforeこのスレッドの各動作.
  • スレッド終了規則:スレッドのすべての操作はhappen-beforeがこのスレッドの終了を検出し、Thread.join()メソッドの終了、Thread.isAlive()の戻り値などの手段により、スレッドの実行が終了したことが検出された.
  • スレッド割込み規則:スレッドinterrupt()メソッドの呼び出しhappen-beforeは、割込みスレッドのコードによって割込みが検出されたときのイベントの発生で発生する.
  • オブジェクト終端規則:オブジェクトの初期化完了(コンストラクション関数実行終了)happen-beforeそのfinalize()メソッドの開始.
  • 伝達性:A happen-before操作B、B happen-before操作Cが操作されると、A happen-before操作Cが得られる.

  • volatileは一定の秩序性を保証することができ、原子性を保証することはできない.
    synchronized,lockは単一スレッドの実行を保証するので,肯定的に秩序化されている
    セキュリティポリシー
    可変オブジェクト
  • Collections.unmodifiableXXX:Collection、List、Set、Map…
  • Guava:ImmutableXXX:Collection、List、Set、Map

  • スレッド閉鎖
  • スタック閉鎖:ローカル変数、同時問題なし
  • ThreadLocalスレッド閉鎖:特に良い閉鎖方法
  • Filterを使用してユーザ情報をThreadLocalの
  • に保存する.
  • 業務方法における処理
  • の使用
  • 方法の実行後にインターセプタ(Inteceptor)を使用して
  • を除去する.
    同期コンテナ:
  • ArrayList -> Vector, Stack
  • HashMap->HashTable(key,valueはnullにできない)
  • Collections.SynchronizedXXX(List,Set,Map)

  • コンカレントコンテナ
  • ArrayList -> CopyOnWriteArrayList
  • HashSet/TreeSet -> CopyOnWriteArraySet/ConcurrentSkipListSet
  • HashMap/TreeMap -> ConcurrentHashMap/ConcurrentSkipListMap

  • ベストプラクティス
  • ローカル変数
  • を使用
  • 可変クラス
  • を使用
  • ロックの役割ドメイン範囲
  • を最小化する.
  • は、直接new Thread
  • ではなく、スレッドプールのExecutorを使用します.
  • 同期を使用してもスレッドのwaitとnotify
  • を使用しないでください.
  • BlockingQueueを使用して生産-消費モデル
  • を実現
  • は、ロックされた同期集合
  • ではなく、同時集合を使用する.
  • Semaphoreを使用して境界付きアクセスを作成する
  • 同期コードブロックを使用するよりも、同期方法
  • を使用しない.
  • 静的変数の使用を避ける(使用時にfinalを加えることが望ましい)