ソフトウェアアーキテクチャ-一連の並行プログラミングアトミック&コレクションの分散

アトミック操作のクラスはjavaで提供され、Atomicのパッケージ名はjava.util.concurrent.atomicです。このパッケージは、アトミック変数の一連の操作クラスを提供します。これらのクラスは、マルチスレッド環境で、スレッドがアトミックメソッドを実行しているときに、他のスレッドによって中断されないようにし、他のスレッドは回転のようになります。ロック同じですが、メソッドの実行が完了するまで、JVMは実行待ちのキューからスレッドを選択します。

ソフトウェアアーキテクチャ-一連の並行プログラミングアトミック&コレクションの分散

アトミック

  • 場合

    アトミックパッケージの下でのこれらのアトミック操作クラスの実装原理を理解するには、最初にCAS操作とは何かを理解する必要があります。

1. CASは、メモリ内の共有データを操作するための特別な命令を指します。これは、最新のCPUで広くサポートされています。この命令は、メモリ内の共有データに対してアトミックな読み取りおよび書き込み操作を実行します。Java並行アプリケーションでは、通常、CompareAndSwapまたはCompareAndSet、つまり、並行アルゴリズムを実装するときに一般的に使用される手法である比較と交換を指します。java.util.concurrentパッケージは、CASを使用して、同期ロックとは異なるオプティミスティックロックを実装します。楽観的ロックは、データをフェッチするたびにデータが変更されないことを楽観的にするため、このプロセスはロックされませんが、更新時に、この期間のデータが更新されたかどうかが判断されます。

2.CASの考え

CASには、現在のメモリ値V、古い期待値A、および更新される値Bの3つのパラメータがあります。期待値Aとメモリ値Vが同じである場合に限り、メモリ値はBに変更されます。 trueを返します。それ以外の場合は何もしません。falseを返します。

3.CASの長所と短所

システムは、ハードウェアレベルでのCAS操作のアトミック性を保証し、現在のスレッドをロックしません。その効率は非常に高いです。ただし、同時実行性が高い状況では、障害の数が増加します。CASが長期間失敗すると、CPUオーバーヘッドが大幅に増加するため、CASは、競合が非常に頻繁に発生するシナリオには適していません
。CASのみが可能です。共有変数のアトムを保証します。操作:複数の共有変数を操作する場合、操作のアトミック性は保証できません。現時点では、ロックを使用するか、複数の共有変数を1つの共有変数に組み合わせて操作できます。JDKは、参照されるオブジェクトのアトミック性を確保するためにAtomicReferenceクラスを提供します。1つのオブジェクトに複数の変数を配置してCAS操作を実行できます。

  • ABA

    CASは、値の操作時に値が変更されたかどうかを確認し、変更がない場合にのみ更新します。ただし、値が元々Aで、Bになり、次にAになる場合、CASはチェック時に値が変更されていないと見なしますが、実際には変更されています。ABA問題の解決策は、バージョン番号を使用することです。変数の前にバージョン番号を追加し、変数が更新されるたびにバージョン番号に1を追加すると、A-B-Aは1A-2B-3Aになります。JDKは、ABA問題を解決するためのAtomicStampedReferenceを提供します

*アトミックメンバー

アトミックメンバーは4つの部分に分かれています

1.アトミック更新基本型
AtomicBoolean:アトミック更新ブール型
AtomicInteger:アトミック更新整数型
AtomicLong:アトミック更新長整数型

2.
AtomicIntegerArray:AtomicIntegerArray:
AtomicLongArray:AtomicLongArray:
AtomicReferenceArray:AtomicReferenceArray:AtomicReferenceArray:AtomicIntegerArray:AtomicLongArray:AtomicReferenceArray:AtomicReferenceArray:AtomicIntegerArray:AtomicIntegerArray:AtomicLongArray :AtomicReferenceArray:AtomicReferenceArray:AtomicReferenceArray:AtomicIntegerArray:AtomicIntegerArray:AtomicLongArray:AtomicReferenceArray:AtomicReferenceArray

3.アトミック更新参照
AtomicReference:アトミック更新参照タイプ
AtomicReferenceFieldUpdater:アトミック更新参照タイプフィールド
AtomicMarkableReference:マークビット付きのアトミック更新参照タイプ

4.
AtomicIntegerFieldUpdater:AtomicIntegerFieldUpdater:
AtomicLongFieldUpdater:AtomicLongFieldUpdater:
AtomicStampedReference:AtomicStampedReference:AtomicIntegerFieldUpdater:AtomicLongFieldUpdater:AtomicStampedReference:AtomicStampedReference:Atomic

CAS
ノンブロッキングアルゴリズムの長所と短所、ABA問題、大きなループオーバーヘッド、および共有変数のアトミック操作のみを保証します。

ConcurrentHashMap

スレッドセーフマップには、HashtableとSynchronizedMapのほか、concurrentHashMapとConcurrentHashMapで使用されるロックセグメンテーションテクノロジーが含まれ、ロックの粒度を調整することでロックの競合を減らします。

ソフトウェアアーキテクチャ-一連の並行プログラミングアトミック&コレクションの分散

不十分:サイズの計算結果をトラバースする必要があります

コピーオンライト

コピーオンライトはCOWと略されます。基本的な考え方は、誰もが最初から同じコンテンツを共有しているということです。誰かがコンテンツを変更したい場合、コンテンツはコピーされて新しいコンテンツを形成してから変更されます。、これは遅延怠惰戦略。
JDK1.5以降、Java同時実行パッケージは、CopyOnWriteメカニズムを使用して実装された2つの並行コンテナーを提供します。それらはCopyOnWriteArrayListとCopyOnWriteArraySetです。

CopyOnWriteコンテナーの
概要CopyOnWriteコンテナーは、コピーオンライト用のコンテナーです。コンテナに要素を追加するとき、現在のコンテナに直接追加するのではなく、最初に現在のコンテナをコピーして新しいコンテナをコピーし、次に要素を新しいコンテナに追加するというのが一般的な理解です。要素を追加した後、次に元のコンテナの参照を新しいコンテナにポイントします。これの利点は
、現在のコンテナーが要素を追加しないため、ロックせずにCopyOnWriteコンテナーによって複雑に読み取ることができることです。
したがって、CopyOnWriteコンテナーは、読み取りと書き込みを分離し、読み取りと書き込み用に異なるコンテナーを使用するというアイデアでもあります。

  • シーン

    黒と白のリスト、より多くを読み、より少なく(めったに変更されない)シーン、製品SKU製品カテゴリを書き込みます。

*長所と短所

メモリを占有し(書き込み中に新しい2つのオブジェクトをコピーします)、リアルタイムのデータ整合性を保証できません。

BlockingQueue

BlockingQueueのキューの長さは、プロデューサーとコンシューマーの速度の差が大きくなりすぎないようにし、メモリの枯渇を防ぐために制限されています。キューの長さは、設定後に変更することはできません。キューに入るときにキューがいっぱいになったとき、またはキューを出るときにキューが空になったとき、さまざまな機能の効果。
シナリオ:生産と消費

例外を報告する場合があります ブール値を返す 待機をブロックする可能性があります 待ち時間を設定できます
チームに参加する add(e) オファー(e) put(e) オファー(e、タイムアウト、ユニット)
チームを離れる 削除する() poll() 取る() 投票(タイムアウト、単位)
見る 素子() ピーク() 番号 番号

ソフトウェアアーキテクチャ-一連の並行プログラミングアトミック&コレクションの分散

  • ArrayBlockingQueue

    配列の実装に基づく制限付きブロッキングキュー。キューのサイズは作成後に変更できません。これは制限付きブロッキングキューであり、その内部実装は配列です。境界とは、容量が制限されていることを意味します。初期化時に容量を指定する必要があります。容量を指定すると、変更することはできません。

  • LinkedBlockingQueue

    リンクリストに基づいて実装された***(指定可能)ブロッキングキューのデフォルトサイズはInteger.MAX_VALUEです。これは、スループットは良好ですが、予測可能性は低くなります。

  • PriorityBlockingQueue

    優先度***のブロッキングキューはnullの挿入を許可せず、すべての要素が比較可能である必要があります(つまり、Comparableインターフェイスを実装します)。順序:非先入れ先出し。

  • SynchronousQueue

    要素が1つだけの同期キュー。キューに要素がある場合、キュー内の要素が他のスレッドによって削除されるまで、挿入操作はブロックされます。

  • DelayQueue

    ***ブロッキングキュー。各要素には遅延時間があり、要素は遅延時間後に解放されます。ブロッキングしているのはその内部要素です。DelayQueueの要素はjava.util.concurrent.Delayedインターフェースを実装する必要があります。このインターフェースの定義は非常に単純です。

  • ConcurrentLinkedQueue

    リンクリストに基づくノンブロッキングキュー。

BlockingDeque

スレッドは要素を生成し、キューの両端に要素を挿入します。現在のキューがいっぱいの場合、要素を削除するスレッドがキューから要素を削除するまで、挿入スレッドはブロックされます。同様に、キューが現在空の場合、要素を削除するスレッドは、挿入スレッドが要素をキューに挿入するまでブロックされます。

ソフトウェアアーキテクチャ-一連の並行プログラミングアトミック&コレクションの分散

ThreadLocalスレッドローカル変数

JavaのThreadLocalクラスを使用すると、同じスレッドでのみ読み取りと書き込みができる変数を作成できます。したがって、コードにThreadLocal変数への参照が含まれている場合、2つのスレッドがこのコードを同時に実行しても、互いのThreadLocal変数にアクセスすることはできません。

マルチスレッド制御ツール

package com.tl.executor;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;

public class TljucUtil {

    /**
     * 测试耗时
     *
     * @param nThreads
     *            线程数
     * @param task
     *            执行任务
     * @param singleNum
     *            单个线程执行个数
     * @return
     * @throws InterruptedException
     */
    public static long timeTasks(int nThreads, int singleNum,
                                 final Runnable task) {
        final CountDownLatch startGate = new CountDownLatch(1);
        final CountDownLatch endGate = new CountDownLatch(nThreads);
        ThreadFactory tf = Executors.defaultThreadFactory();
        final int singleExeNum = singleNum == 0 ? 1 : singleNum;
        final AtomicLong sum = new AtomicLong();
        final AtomicLong min = new AtomicLong(10000);
        final AtomicLong max = new AtomicLong(0);
        for (int i = 0; i < nThreads; i++) {
            tf.newThread(new Thread() {
                @Override
                public void run() {
                    try {
                        startGate.await();
                        for (int j = 0; j < singleExeNum; j++) {
                            long start = System.nanoTime();
                            try {
                                task.run();
                            } finally {
                                long end = System.nanoTime();
                                long at = ((end - start) / 1000 / 1000);
                                sum.addAndGet(at);
                                if (min.get() > at) {
                                    min.getAndSet(at);
                                }
                                if (max.get() < at) {
                                    max.getAndSet(at);
                                }
                            }
                        }
                        endGate.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        long start = System.nanoTime();
        startGate.countDown();
        try {
            endGate.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        long at = ((end - start) / 1000 / 1000);

        int allCount = singleExeNum * nThreads;

        System.out.println("执行任务数:" + allCount);
        System.out.println("------------------------");
        System.out.println("所有线程共耗时:" + transStr(sum.get()));
        System.out.println("并发执行完耗时:" + transStr(at));
        System.out
                .println("单任务平均耗时:" + transStr((double) sum.get() / allCount));
        System.out.println("单线程最小耗时:" + transStr(min.get()));
        System.out.println("单线程最大耗时:" + transStr(max.get()));
        return end - start;
    }

    public static String transStr(long ms) {
        return transStr((double) ms);

    }

    public static String transStr(double ms) {
        if (ms < 1000) {
            return ms + " ms";
        }
        double s = ms / 1000;
        if (s < 1000) {
            return s + " s";
        }
        double m = s / 60;
        if (m < 60) {
            return m + " m";
        }
        double h = m / 60;
        if (h < 24) {
            return h + " h";
        }

        double d = h / 24;
        if (d < 30) {
            return d + " d";
        }
        return d + " d";
    }
}
  • 呼び出しメソッドのデモンストレーション
    
    package com.tl.executor.threadlocal;

インポートcom.tl.executor.TljucUtil;

インポートjava.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

パブリッククラスThreadlocalDemo1はRunnableを実装します{

static ThreadLocal<SimpleDateFormat> tl=new ThreadLocal<SimpleDateFormat>();
static SimpleDateFormat simpleDateFormat=null;

public static void main(String[] args) {
    TljucUtil.timeTasks(100,1,new ThreadlocalDemo1());
}

@Override
public void run() {
    try {
        if(tl.get()==null){
            tl.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        }
        Date date=tl.get().parse("2017-11-28 22:45:11");
        Thread.sleep(100);
        System.out.println(date);
    } catch (ParseException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}



PS:基本本次就讲解这么多吧,内容比较丰富,代码这块可以根据相关的名称百度搜一些大神的源码来看,但是理论一定要理解,一通百通。来不及握手拜了个拜~下次见。

おすすめ

転載: blog.51cto.com/12040702/2667922