並行プログラミングの研究ノート(B) - JDKと契約

免責事項:この記事はブロガーの元で、許可ブロガーなく再生してはなりません。https://blog.csdn.net/weixin_36904568/article/details/90347276

A:同期制御

1.同期拡張 - リエントラントロック

繰り返し同じスレッドが同期シンクブロックよりも柔軟性と、ロックすることができます入力してください。

  • ロックを作成します。
    • 非公平:ReentrantLockのロック=新しいReentrantLockの();
    • 公平:ReentrantLockのロック=新しいReentrantLockの(真の);
  • ロック:lock.lock();
    • 使用して、ロックを解除することができますlock.lockInterruptibly();
    • ロックの使用を制限:lock.tryLock(時間、TimeUnitで単位)。
  • ロックを解除:lock.unlock();
    • )(lock.isHeldByCurrentThread:それはロックを持っているかどうかを確認

(1)同期の問題との契約を

重要なリソースのロック、ミューテックス同期

import java.util.concurrent.locks.ReentrantLock;

public class ReenterLockTest implements Runnable{
    public static ReentrantLock lock = new ReentrantLock();

    public static int i;

    @Override
    public void run() {
        for (int j = 0; j < 10; j++) {
            lock.lock();
            try {
                i++;
            }
            finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReenterLockTest test = new ReenterLockTest();
        Thread a = new Thread(test);
        Thread b = new Thread(test);
        a.start();
        b.start();
        a.join();
        b.join();
        System.out.println(i);
    }
}

(2)割り込み応答

ロックを待っている間、スレッドを待っていると、割り込みレイテンシを通知することができます

 public void run() {
        for (int j = 0; j < 10; j++) {
            try {
                lock.lockInterruptibly();
                i++;
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("被通知不用等待了");
            }
            finally {
                if (lock.isHeldByCurrentThread())
                     lock.unlock();
                System.out.println("线程退出");
            }
        }
    }

(3)アプリケーションロック待ち限界

  • パラメータがありますいくつかの時間のためのスレッドとロックが利用できない場合、あなたは放棄することができます
  • いいえパラメータん:使用できない、直接利用可能に直接与えるかどうかを判断します
public void run() {
        for (int j = 0; j < 10; j++) {
            if (lock.tryLock()) {
                //业务代码
                try {
                    i++;
                } finally {
                    if (lock.isHeldByCurrentThread())
                        lock.unlock();
                    System.out.println("线程退出");
                }
            }
        }
    }

(4)フェアロック

デフォルト不公平ロック、我々はスレッドを取得する傾向があるが、すでに、再び高効率なロックを保持している
大きな支出、各スレッドがロックへのアクセス権を持っている、フェアロックを

2.リエントラントロックパートナー - 条件

特定の時間に通知を待ってロックしてみましょう

  • 作成:条件条件= lock.newCondition()
  • 待って
    • 待っています():特定のロックを取得した後、スレッドが中断されたときにロックが解除されている間に待機するスレッドが待機するようにジャンプすることができます
    • awaitUninterruptibly():応答のためのスレッドを待機を中断することはできません
  • お知らせ
    • 信号():特定のロックを取得した後、待機中のスレッドを覚ます、レリーズロック
    • signalAll():特定のロックを取得した後、すべての待機中のスレッドを覚ます、ロックを解除
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ReenterLockTest implements Runnable {
    public static ReentrantLock lock = new ReentrantLock();
    public static Condition condition = lock.newCondition();
    public static int i;

    @Override
    public void run() {
        if (lock.tryLock()) {
            //业务代码
            try {
                i++;
                System.out.println("开始等待");
                condition.await();
                System.out.println("被唤醒了");
            } catch (InterruptedException e) {
                System.out.println("我在等待时被中断了");
            } finally {
                if (lock.isHeldByCurrentThread())
                    lock.unlock();
                System.out.println("线程退出");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReenterLockTest test = new ReenterLockTest();
        Thread a = new Thread(test);
        a.start();
        //等待线程A把锁释放,否则拿不到锁
        Thread.sleep(2000);
        lock.lock();
        condition.signal();
        lock.unlock();
        System.out.println(i);
    }
}

3.ロック機能拡張 - セマフォセマフォ

これは、重要なリソースと同じリソースへの複数の同時アクセスを可能にします

  • 初出:
    • スレッドの数が同時にアクセス指定:セマフォセマフォ=新しいセマフォ(スレッド)
    • セマフォセマフォ=新しいセマフォ(スレッド数、真の):スレッドと公正なロックの数を指定します。
  • クリティカルセクションを入力します。
    • semaphore.acquire():クリティカルゾーンに入るための許可を取得し、取得したり、待つの失敗が中断されます
    • semaphore.acquireUninterruptibly():割り込みに応答することができない、待ち時間を取得するために失敗し、クリティカルセクションを入力するための許可を取得しよう
    • semaphore.tryAcquire():終了を取得するために失敗し、クリティカルセクションを入力するための許可を取得しよう
  • クリティカルセクションを終了します。
    • semaphore.release():リソースへの放出許可アクセスした後、
import java.util.concurrent.Semaphore;

public class SemaphoreTest {

    static Semaphore semaphore = new Semaphore(5);

    static class MyThread extends Thread{

        @Override
        public void run() {
            try {
                semaphore.acquire();
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getId()+"正在执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            finally {
                semaphore.release();
            }
        }
    }
    public static void main(String[] args) {
        MyThread[] threads = new MyThread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new MyThread();
            threads[i].start();
        }
    }
}

4.読み書きロックを - ReadWriteLock

別々の読み取りと書き込みのロックが効果的に、ロックの競合を減らすのに役立ち、システムのパフォーマンスを向上させることができます。
複数のスレッドが同時に読むことができます、閉塞を引き起こすが、それでもロックを待機して保持する必要がある間の動作を読み書きしません。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLock {
    private static ReentrantLock myLock = new ReentrantLock();
    private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private static Lock readLock = lock.readLock();
    private static Lock writeLock = lock.writeLock();

    static class ReadThread extends Thread{
        @Override
        public void run() {
//            myLock.lock();
            readLock.lock();
            System.out.println("开始大量读操作");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            finally {
//                myLock.unlock();
                readLock.unlock();
            }
        }
    }
    static class WriteThread extends Thread{
        @Override
        public void run() {
            try {
//                myLock.lock();
                writeLock.lock();
                System.out.println("开始写操作");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
//                myLock.unlock();
            }

        }
    }

    public static void main(String[] args) {
        ReadThread[] readThreads = new ReadThread[10];
        for (int i = 0; i < readThreads.length; i++) {
            readThreads[i] = new ReadThread();
            readThreads[i].start();
        }
        WriteThread[] writeThreads = new WriteThread[2];
        for (int i = 0; i < writeThreads.length; i++) {
            writeThreads[i] = new WriteThread();
            writeThreads[i].start();
        }
    }
}

5.ダウンカウンタ--CountDownLatch

スレッドその後、待機中のスレッドを続行することができ、カウント-1作業を完了するためのスレッドがあるたびに、すべてのスレッドがカウントが0で作業を完了したときにカウントダウン実行終了後まで待ちます

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {
    private static CountDownLatch latch = new CountDownLatch(2);
    static class CheckThreadA extends Thread{
        @Override
        public void run() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程A检查完毕");
            latch.countDown();
        }
    } static class CheckThreadB extends Thread{
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程B检查完毕");
            latch.countDown();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        CheckThreadA a = new CheckThreadA();
        CheckThreadB b = new CheckThreadB();
        a.start();
        b.start();
        //等待所有线程完成工作后,主线程才能继续进行
        latch.await();
        System.out.println("都检查好了");
    }
}

6.サイクルフェンス--CyclicBarrier

再利用可能なダウンカウンタ

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierTest {
    //主线程需要完成的所有任务
    static class BarrierThread extends Thread{

        private int flag;

        public BarrierThread(int flag) {
            this.flag = flag;
        }

        @Override
        public void run() {
            switch (flag){
                case 0:
                    System.out.println("第一个任务完成!");
                    flag = 1;
                    break;
                case 1:
                    System.out.println("第二个任务完成!");
                    flag = 2;
                    break;
                default:
                    System.out.println("任务失败!");
                    break;
            }
        }
    }
    
    //子线程需要分别完成的任务
    static class MyThread extends Thread{

        private String name;

        private CyclicBarrier barrier;

        public MyThread(CyclicBarrier barrier,String name) {
            this.barrier = barrier;
            this.name = name;
        }

        @Override
        public void run() {
            try {
                System.out.println(name+"开始第一个工作");
                Thread.sleep(Math.abs(new Random().nextInt()%10000));
                //等待其他线程一起完成第一个工作
                barrier.await();
                System.out.println(name+"开始第二个工作");
                Thread.sleep(Math.abs(new Random().nextInt()%10000));
                //等待其他线程一起完成第二个工作
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        int N = 10;
        CyclicBarrier barrier = new CyclicBarrier(N,new BarrierThread(0));
        MyThread[] threads = new MyThread[N];
        for (int i = 0; i < N; i++) {
            threads[i] = new MyThread(barrier,"线程"+i);
            threads[i].start();
        }
    }
}

7.スレッドは、ツールをブロック--LockSupport

スレッドがブロックされていることを確認するために、任意の位置で実施することができる、と突然終了するには、同じスレッドを再開しないであろうと、それは(中断は黙って終了)割り込み例外を発生しません、同じロックオブジェクトを取得するのを待つ必要はありません。

  • ブロッキング:lock.parkを()、lock.parkNanos(時間)、lock.parkUntil(時間)
    • クリティカルセクションのリソースが利用可能であり、リターンはすぐに使用できなくなった場合(1つだけ入力することができます)
    • 重要な地域資源が利用できない場合、ブロック
  • lock.unparkを():ウェイク
    • クリティカルエリアリソースが利用可能になります

8.ツールを制限--RateLimiter

  • リーキーバケットアルゴリズム:要求がシステムに入ったときに記憶領域を使用して、関係なく、レート要求の、第1の記憶領域に格納され、その後、固定速度記憶領域処理に流出します
  • トークンバケットアルゴリズム:トークンバケットに保存された単位時間当たりの発生量、要求のみを処理するトークンを取得するためのプログラム、要求が(クラッシュを避けるために)落とされるまたはトークンを待ちます

トークンバケットアルゴリズムを達成RateLimiter

  • 作成:RateLimiterリミッター= RateLimiter.create(トークン数)
  • 限定的
    • トークンを待っ:limiter.acquireを();
    • 要求をドロップ:limiter.tryAcquire();
import com.google.common.util.concurrent.RateLimiter;

public class RateLimiterTest {
    private static RateLimiter limiter = RateLimiter.create(2);
    static volatile int i = 0;
    static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis());
        }
    }

    public static void main(String[] args) {
        MyThread[] threads = new MyThread[10];
        for (int j = 0; j < threads.length; j++) {
//            limiter.acquire();
            if (!limiter.tryAcquire())
                continue;
            threads[i] = new MyThread();
            threads[i].start();
        }

    }
}

II:スレッドプール

1.基本概念

(1)理由

多数のスレッドは、実際の作業時間以上のスレッドの占有スレッドを作成し、破壊することがあり、スレッド回復の多くはGCの休止時間を延長され、CPUとメモリのリソースが不足します

(2)スレッドプール

ユーザーがスレッドを必要とするとき、スレッドプール内の複数のアクティブなスレッドのメンテナンスでは、スレッドが元から作成されたスレッドプールのアイドルからのスレッドに入る、ユーザーが完了すると、スレッドプールのスレッドへの復帰にオリジナルの破壊スレッド

(3)カテゴリー

  • 一般的なスレッドサービス
    • FixedThreadPool:ジョブ投入中のスレッドの固定数の維持、アイドルスレッドがタスクキューに保存されているタスク、またはタスクを実行している場合
    • SingleThreadExecutor:スレッドを維持するため、複数のジョブ投入、タスクキューに格納されている他のタスクがある場合
    • CachedThreadPool:スレッド数の動的変化のメンテナンスは、ジョブの投入時には、アイドル状態のスレッドがある場合は、直接タスクを実行する、または新しいスレッドプールのスレッドが多重化を返した後、タスクを実行する新しいスレッドを作成
  • スレッドのスケジューリングサービス
    • SingleThreadScheduledExecutor:あなたが定期的に実行するタスクまたはタスクの実行を遅らせることができ、スレッドを維持
    • ScheduledThreadPool:複数のスレッドを維持し、タスクが遅れたり、定期的なタスクすることができます

(4)スレッドサービスの使用を

エグゼキューからスレッドファクトリは、必要なサービスExecutorServiceのスレッドまたはスレッドのスケジューリングサービスScheduledExecutorServiceを取得します

1.一般的なスレッドサービス
  • 提出:実行するための特定のタスクを提出し、今後のオブジェクトを返し、その結果の実装
  • 実行:実行するために指定されたタスクを送信、印刷例外情報
  • シャットダウン:拒否、新たに提出されたタスクを受け入れるが、タスクまで実行し続けます
  • shutdownNowの:拒否新しいタスクの提出を受け入れ、実行中のタスクを停止します
  • invokeAll:バッチは、タスクの数を完了するには、すべてのタスクの完了後に返さ
  • invokeAnyは:バッチは、タスクが完了した後に返し、タスクの数を完了します
import java.util.concurrent.*;

public class ThreadPoolTest {

    static class Task implements Runnable {

        @Override
        public void run() {
            System.out.println("当前线程ID为:" + Thread.currentThread().getId());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("我在睡眠时被中断了");
            }
        }
    }


    public static void main(String[] args) {
        Task task = new Task();
        ExecutorService service = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            service.submit(task);
        }
//        service.shutdown();
        service.shutdownNow();
        try {
            Thread.sleep(2000);
            service.submit(task);
        } catch (RejectedExecutionException exception) {
            System.out.println("已经拒绝提交任务了");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2.スレッドのスケジューリングサービス
  • スケジュール:遅延の後タスク
  • scheduleAtFixRate:最後にスケジュールされたタスク(タスクの実行時間がスケジュールされた時間よりも大きい場合は、次のタスクは、タスクの実行後に開始される)の期間にわたってタスクの開始時間後
  • scheduleWithFixDelay:最後の時間の後、一度タスクスケジューラのタスクの期間(タスクの実行時間がスケジュールされた時間よりも大きい場合は、次のタスクの実行タスクの開始後の時間の予定の期間)

タスクが例外である場合は、それ以降の作業を続行することはできません

import java.util.concurrent.*;

public class ThreadPoolTest {

    static class Task implements Runnable {

        @Override
        public void run() {
            System.out.println("当前线程ID为:" + Thread.currentThread().getId());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("我在睡眠时被中断了");
            }
        }
    }


    public static void main(String[] args) {
        Task task = new Task();
        ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
//        service.scheduleAtFixedRate(task,0,2, TimeUnit.SECONDS);
        service.scheduleWithFixedDelay(task,0,2,TimeUnit.SECONDS);
    }
}

2.基本的なスレッドプールの実装

(1)基本的なスレッドプール

スレッドプールは、ThreadPoolExecutorのコンストラクタによって実装されています。

/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters and default thread factory and rejected execution handler.
     * It may be more convenient to use one of the {@link Executors} factory
     * methods instead of this general purpose constructor.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

あなたが見ることができる、以下のように、メインスレッドプールのパラメータは次のとおりです。

  • corePoolSize:スレッド数
  • maximumPoolSize:スレッドの最大数
  • keepAliveTimeが:アイドルスレッドの生存期間
  • 単位:時間の単位
  • ワークキュー:タスクキュー
    • 直接送信キュー(SynchronousQueue):スレッドの最大数は、拒否の方針を取る場合は、noキューの容量、チーム外のチームは、常にタスク実行スレッドに提出される、(新しいスレッドを作成します)
    • バインド・タスク・キュー(ArrayBlockingQueue):固定容量キュー<corePoolSizeは、そうでない使命がキューに参加する、スレッド数に新しいスレッドを作成します。そして、スレッドの数は、<maximumPoolSizeが、それ以外の戦略を取るために、完全な拒否されたタスクキューに新規スレッドを作成します
    • バインドなしタスク・キュー(LinkedBlockingQueue):キューの無制限の容量は、タスクキューが存在しない場合に満ちている、あなたは、システムメモリが不足することがあります。<corePoolSizeが、そうでない使命がキューに参加するときに新しいスレッドを作成するスレッドの数。
    • プライオリティキュー(PriorityBlockingQueue):無制限の特別なキュー、タスクシーケンスを制御することができます
  • defaultThreadFactory:デフォルトのスレッドファクトリは、スレッドを作成します
  • たDefaultHandler:ポリシーを拒否し、デフォルト
    • AbortPolicy:直接スロー例外
    • CallerRunsPolicy:タスク(パフォーマンスの低下)を実行するには、呼び出し側のスレッドで破棄されます
    • DiscardPolicy:静かに仕事を捨てます
    • DiscardOldestPolicy:実行するタスクを破棄するには、ジョブを再提出することを試みます

(2)特定のスレッドプール

newFixedThreadPool:

スレッドの数が同じとスレッドの最大数、無制限のタスクキュー

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

newSingleThreadExecutor:

分解newFixedThreadPool

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

newCachedThreadPool:

スレッドの最大数は、タスクキューを使用して直接提出し、無限です

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

(3)実行スレッドプール

/**
     * Executes the given task sometime in the future.  The task
     * may execute in a new thread or in an existing pooled thread.
     *
     * If the task cannot be submitted for execution, either because this
     * executor has been shutdown or because its capacity has been reached,
     * the task is handled by the current {@code RejectedExecutionHandler}.
     *
     * @param command the task to execute
     * @throws RejectedExecutionException at discretion of
     *         {@code RejectedExecutionHandler}, if the task
     *         cannot be accepted for execution
     * @throws NullPointerException if {@code command} is null
     */
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

タスクを実行するようにスケジュールダイレクト:

if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
  }

タスクキューに参加:

if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
 }

拒否した戦略:

 else if (!addWorker(command, false))
            reject(command);

(4)カスタムスレッドプールのスレッドファクトリ、戦略を拒否しました

import java.util.concurrent.*;

public class MyThreadPoolTest {

    static class Task implements Runnable {

        @Override
        public void run() {
            String group = Thread.currentThread().getThreadGroup().getName();
            String name = Thread.currentThread().getName();
            System.out.println(group+"--"+name);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("我在睡眠时被中断了");
            }
        }
    }

    //初始化一个固定容量的,使用合适的无界任务队列容量的,采用自定义工厂和自定义拒绝策略的线程池
    static class MyThreadPool extends ThreadPoolExecutor{

        public MyThreadPool(int size) {
            super(size, size, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10),new MyThreadFactory(),new MyRejectHandler());
        }
        
        @Override
        protected void beforeExecute(Thread t, Runnable r) {
            System.out.println("准备执行任务:"+r.toString());
    }

        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            System.out.println("任务执行完毕:"+r.toString());
        }

        @Override
        protected void terminated() {
            System.out.println("线程池退出");
        }
    }

    //自定义线程工厂
    static class MyThreadFactory implements ThreadFactory{

        int index = 0;

        ThreadGroup group;

        @Override
        public Thread newThread(Runnable r) {
            //记录创建时间
            System.out.println("正在创建线程---"+System.currentTimeMillis());
            //自定义线程名称,组名,优先级
            group = new ThreadGroup("线程组A");
            Thread thread = new Thread(group,r);
            thread.setName("线程-"+index++);
            thread.setPriority(Thread.NORM_PRIORITY);
            //设置守护线程
            thread.setDaemon(false);
            return thread;
        }
    }

    //自定义拒绝策略
    static class MyRejectHandler implements RejectedExecutionHandler{

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            System.out.println("我拒绝了一个任务"+r.toString());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Task task = new Task();
        ExecutorService service = new MyThreadPool(5);
        for (int i = 0; i < 10; i++) {
            service.submit(task);
            Thread.sleep(100);
        }
        service.shutdown();
    }
}

例外の処理
		@Override
        public void execute(Runnable command) {
            super.execute(wrap(command,clientTrace()));
        }

        @Override
        public Future<?> submit(Runnable task) {
            return super.submit(wrap(task,clientTrace()));
        }

        //提交任务的堆栈信息
        private Exception clientTrace(){
            return new Exception("clientTrace");
        }

        //在捕捉到异常时,打印提交任务时的异常,再打印任务本身的异常
        private Runnable wrap(final Runnable task,final Exception trace){
            return new Runnable() {
                @Override
                public void run() {
                    try {
                        task.run();
                    }
                    catch (Exception e){
                        trace.printStackTrace();
                        throw  e;
                    }
                }
            };
        }

3. ForkJoinスレッドプール

最終的な結果を完了するために待機しているスレッドの他の枝に参加する方法を使用して行われ、小さなタスクを処理した後の大きな課題は、小さなタスクを処理、フォーク方法スレッドでより多くの支店を開きます。

アイドルスレッドは、他のスレッドを助けるその委任の終了時にタスク実行キューが削除され
、新しいタスクやポップがあるまでスレッドがアイドル状態になっている場合にはウェイク、それが中断されますスタックにプッシュ

  • RecursiveAction:なし戻り値タスク
  • RecursiveTask:戻り値を運ぶのタスク
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

public class ForkJoinTest {

    static class Task extends RecursiveTask<Long> {

        private long start;

        private long end;

        public Task(Long start, Long end) {
            this.start = start;
            this.end = end;
        }

        @Override
        protected Long compute() {
            long sum = 0;
            long step = (start + end) / 100;    //分解
            ArrayList<Task> tasks = new ArrayList<>();  //记录子任务
            long index = start;
            //拆分成多个子任务并提交
            for (int i = 0; i < 100; i++) {
                Task task = new Task(index, index+step > end ? end : index+step);
                index = step + 1;
                tasks.add(task);
                task.fork();
            }
            //执行子任务
            for (Task t :
                    tasks) {
                sum += t.join();
            }

            return sum;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ForkJoinPool pool = new ForkJoinPool();
        Task task = new Task(0L, 200000L);
        //提交任务
        ForkJoinTask<Long> result = pool.submit(task);
        //获取返回值
        long res = result.get();
        System.out.println("结果:" + res);
    }
}

4.グアバサポートスレッドプール

  • DirectExecutor:現在のスレッド内で直接実行されるタスクは、統一された同期および非同期呼び出しを呼び出します
  • デーモンスレッドプール:MoreExecutors.getExitingExecutorService(サービス・スレッド)によって、対応する接続​​デーモンスレッドプール現在のスレッドにサービススレッドのプールのように

3:コンカレント・コンテナー

1. HashMapのスレッドセーフ

(1)コレクションのパッケージ

Map map = Collections.synchronizedMap(new HashMap<>());
  • シンプル
  • マルチスレッド環境のパフォーマンスの低下

(2)のConcurrentHashMap

ConcurrentHashMap map = new ConcurrentHashMap();

また、デフォルトの16個のセグメント、いくつかの小さな内部HashMapを細分化。対応するロッキングセグメントとハッシュロックのハッシュ値を取得する際

  • 高い同時実行に適し

(3)テーブルSkipListジャンプ

ConcurrentSkipListMap map = new ConcurrentSkipListMap();

基本的に、階層、順序付けられたリストを維持するために、データ構造をすばやく見つけます

テーブルは、ランダムアルゴリズムを挿入することにより、唯一の局所変形を修正する
下層ジャンプクエリルックアップテーブルを上位層から

作曲:

ノードは、キーと値のペアを保持しています

static final class Node<K,V> {
        final K key;
        volatile Object value;
        volatile Node<K,V> next;

        /**
         * Creates a new regular node.
         */
        Node(K key, Object value, Node<K,V> next) {
            this.key = key;
            this.value = value;
            this.next = next;
        }
  }

次のノードを指すキーを設定するCASノード動作によって

		boolean casValue(Object cmp, Object val) {
            return UNSAFE.compareAndSwapObject(this, valueOffset, cmp, val);
        }

        /**
         * compareAndSet next field
         */
        boolean casNext(Node<K,V> cmp, Node<K,V> val) {
            return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
        }

インデックスノードを保存

static class Index<K,V> {
        final Node<K,V> node;
        final Index<K,V> down;
        volatile Index<K,V> right;

        /**
         * Creates index node with given values.
         */
        Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) {
            this.node = node;
            this.down = down;
            this.right = right;
        }
}

CASの動作により、インデックスの右向き

        /**
         * compareAndSet right field
         */
        final boolean casRight(Index<K,V> cmp, Index<K,V> val) {
            return UNSAFE.compareAndSwapObject(this, rightOffset, cmp, val);
        }

インデックスヘッド

static final class HeadIndex<K,V> extends Index<K,V> {
        final int level;
        HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) {
            super(node, down, right);
            this.level = level;
        }
    }

2.スレッドセーフリスト

(1)ベクトル

Vector vector = new Vector();
  • パフォーマンスが低下

(2)コレクションパッケージ

List list = Collections.synchronizedList(new ArrayList<>());
  • マルチスレッド環境のパフォーマンスの低下

(3)ConcurrentLinkedQueue

ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();

CASの設定値、次のノード、ノード頭尾を用いて

	boolean casItem(E cmp, E val) {
            return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
        }
	boolean casNext(Node<E> cmp, Node<E> val) {
            return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
        }
    private boolean casTail(Node<E> cmp, Node<E> val) {
        return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
    }

   	private boolean casHead(Node<E> cmp, Node<E> val) {
        return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
    }
  • 一つのノードのみ:成功を確保するための新たなサイクルのノードの挿入
  • 複数のノード:ノードを介して最後のループを検索し、挿入、テール・ノードを更新します
  • センチネルノード:再循環さ最後のノードを見つける必要かのノードではありません
/**
     * Inserts the specified element at the tail of this queue.
     * As the queue is unbounded, this method will never return {@code false}.
     *
     * @return {@code true} (as specified by {@link Queue#offer})
     * @throws NullPointerException if the specified element is null
     */
    public boolean offer(E e) {
        checkNotNull(e);
        final Node<E> newNode = new Node<E>(e);

        for (Node<E> t = tail, p = t;;) {
            Node<E> q = p.next;
            if (q == null) {
                // p is last node
                if (p.casNext(null, newNode)) {
                    // Successful CAS is the linearization point
                    // for e to become an element of this queue,
                    // and for newNode to become "live".
                    if (p != t) // hop two nodes at a time
                        casTail(t, newNode);  // Failure is OK.
                    return true;
                }
                // Lost CAS race to another thread; re-read next
            }
            else if (p == q)
                // We have fallen off list.  If tail is unchanged, it
                // will also be off-list, in which case we need to
                // jump to head, from which all live nodes are always
                // reachable.  Else the new tail is a better bet.
                p = (t != (t = tail)) ? t : head;
            else
                // Check for tail updates after two hops.
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }

(4)効率的な読み出し

CopyOnWriteArrayListと:同時実行をロックすることにより、高いパフォーマンス

CopyOnWriteArrayList list = new CopyOnWriteArrayList();
  • のみ書き込みや書き込み操作をブロック
    • 読み取りの間:同時ではありません
    • 書き込みの間および読み取り操作:データロックのコピー、書き込み、その後、元のデータを交換し、ロックを解除します。変更されたデータは、揮発性の使用であるので、視認性を有しています。

CopyOnWriteLinkedQueue:CASによって同時実行操作

(5)データ共有チャネルのBlockingQueue

スレッド間のデータ共有

  • ArrayBlockingQueue:有界
  • LinkedBlockingQueue:无界

チームへ:

  • 提供:キューがいっぱいの場合、偽
  • 置く:キューがいっぱい待っている場合

チーム:

  • 世論調査:キューが空またはnullの場合
  • 取る:キューが空で待機している場合

インプリメンテーション:条件チームの管理とチームリエントラントロックと条件変数条件から(プロデューサー - 消費者モデル)

おすすめ

転載: blog.csdn.net/weixin_36904568/article/details/90347276