Java スレッドの概要 (中級および上級)

プロセスとは:

        ソフトウェアとして理解している

スレッドとは:

        ソフトウェアの機能として、それが何をするのかを理解します。

マルチスレッドとは:

        ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​:ソフトウェア内の特定の機能以前は一人で完成させるのが大変でしたが、今では複数人で協力して簡単に楽しく完成させることができます。

安全でないスレッドとは:

        ソフトウェアの特定の機能として理解されています。元々は 1 人が頑張って完成させたものでした。大変ではありましたが、データは間違っていませんでした。しかし、現在は複数人で作業しています。その結果、Zhang San は The を読みました。 data は Li Si によって変更されたデータであり、最新のデータではないため、スレッド安全ではありません。

スレッドセーフとは:

         ソフトウェアの「ある機能」として理解しています。以前は一人で頑張って完成させていました。大変ではありましたが、データは間違っていなかったのですが、今は複数人で一緒にやっているのです。並んでくださいとお願いしました。 . データを 1 つずつ整然と操作します。誰かが操作している場合は、外でお待ちいただくようお願いします。前の人が作業を終えるまで待ってから入る必要があります。

並列処理とは:

        複数のタスクを同時に実行するソフトウェア内の特定の機能として理解します。

同時実行性とは:

        ソフトウェアの特定の機能として理解されていますが、一度に多くのリクエストが届き、それが処理されない場合、プログラムがクラッシュしたり、フリーズしたりすることがあります。

安全でないスレッドと安全なスレッド、

ArrayList HashSet HashMap

ArrayLists はスレッドセーフです。Vector、Collections、CopyOnWriteArrayList スレッドセーフ

HashSet はスレッドセーフではありません。 CopyOnWriteArraySet スレッドセーフ

HashMap はスレッドセーフではありません。ConcurrentHashMap スレッドセーフ

コードを使用してマルチスレッドをシミュレートしてみましょう。

要件: 切符の販売と施錠には 4 人の車掌が必要です

package com.japhet.util;

//卖票
class Ticket{

    //总票数
    private int ticket = 3000;

    //卖票逻辑递减
    public synchronized void seal(){
        if(ticket<=0){
            return;
        }
        ticket--;
        System.out.println(Thread.currentThread().getName()+"票还剩余"+ticket);
    }

}

public class ThreadUtils {
    public static void main(String[] args) {

        Ticket ticket = new Ticket();
        //售票员1进行卖票
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 4000; i++) {
                    ticket.seal();
                }
            }
        },"AA").start();
        //售票员2进行卖票
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 4000; i++) {
                    ticket.seal();
                }
            }
        },"BB").start();

        //售票员3进行卖票
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 4000; i++) {
                    ticket.seal();
                }
            }
        },"CC").start();

        //售票员4进行卖票
        new Thread(()->{
            for (int i = 0; i < 4000; i++) {
                ticket.seal();
            }
        },"DD").start();

    }
}

次のコードは、スレッド間の通信をシミュレートするために使用されます。

要件: マルチスレッドであるため、11 番目のスレッドがリソースを取得して独自のプログラムを実行するとき、次回誰がリソースを取得できるかはわかりません。その後、誰がリソースを取得できるかを設定できます (await シグナルを介して待機すると通知されます)実行するスレッド)、ここではスレッド 11 を実行、スレッド 22 を実行、スレッド 22 を実行、スレッド 33 を実行、スレッド 33 を実行、スレッド 11 を実行し、このアクションを順番に 10 回ループします。

package com.japhet.util;

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

class Srouce{
//    线程标识
    private int temp = 1;

//    锁
    private Lock lock =  new ReentrantLock();

//    三个线程
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    public void test(){
        lock.lock();
        try {
            while (temp!=1){//防止虚假唤醒,所以得用while循环
                condition1.await();
            }

            System.out.println("线程1111进来了");

            temp = 2;//修改标识,
            condition2.signal();//通知线程2去干活
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void test2(){
        lock.lock();
        try {
            while (temp!=2){//防止虚假唤醒,所以得用while循环
                condition2.await();
            }
            System.out.println("线程2222进来了");
            temp = 3;//修改标识,
            condition3.signal();//通知线程2去干活
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void test3(){
        lock.lock();
        try {
            while (temp!=3){//防止虚假唤醒,所以得用while循环
                condition3.await();
            }
            System.out.println("线程3333进来了");
            temp = 1;//修改标识,
            condition1.signal();//通知线程2去干活
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

public class ThreadUtils2 {
    public static void main(String[] args) {
        Srouce srouce = new Srouce();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                srouce.test();
            }
        },"11").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                srouce.test2();
            }
        },"22").start();


        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                srouce.test3();
            }
        },"33").start();
    }

}

デッドロックとは

複数のスレッドが同時に操作を実行する

スレッド A がロック A を保持します

スレッド B はロック B を保持します

プログラムがまだ進行中で、ロックが解放されていない場合、スレッド A はスレッド B のコンテンツを取得しようとします。同時に、スレッド B もスレッド A のコンテンツを取得しようとします。このとき、スレッド A とスレッドB はお互いを待っている途中なので、無限ループに陥ります。あなたは私が解放されるのを待ち、私はまたあなたが解放されるのを待ちます。

公平なロックと不公平なロックとは何ですか

不公平なロック: (高速、高効率)

        たとえば、3 つのスレッド A、B、C があります。これを実行すると、スレッド A の機能が強力になる可能性があります。その後、一連のプログラムが実行されると、スレッド A が実行される可能性が非常に高くなりますが、他のスレッド B は実行されます。と C は基本的にあまり動かない、餓死する、これが不公平なロックの概念です

フェア ロック: (遅い、低効率)

        たとえば、A、B、C という 3 つのスレッドがあり、実行すると、比較的公平に各スレッドに分散されます。ソース コードを参照してください。 

 

FutureTask 将来タスクのデモ

package com.japhet.util;

import java.util.concurrent.FutureTask;

public class FutureTaskDemo {
    public static void main(String[] args) {
        System.out.println("主线程开始....."+Thread.currentThread().getName());
        FutureTask<Integer> futureTask = new FutureTask<>(()->{
            System.out.println("--------"+Thread.currentThread().getName());
            return 1;
        });
        new Thread(futureTask).start();
        try {

            while (!futureTask.isDone()){
                System.out.println("线程还没有执行完,等待...."+Thread.currentThread().getName());
            }


            Integer num = futureTask.get();
            Integer num2 = futureTask.get();

            System.out.println("最后结果 "+num);
            System.out.println("最后结果2 "+num2);

            System.out.println("主线程结束....."+Thread.currentThread().getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

レンダリング

ブロッキングキュー(BlockingQueue)とは

いわゆるキュー: つまり、先入れ先出し方式であり、キューにはサイズ制限があります。

スレッド A は要素をキューに入れることができ、スレッド B はキュー内の要素を取得できます。スレッド A によってキュー要素がいっぱいになると、スレッド A はブロックされます。スレッド B がキュー内のデータの取得を完了すると、スレッド B はブロックされます。ブロックされることもあります。 

スレッド プール (エグゼキュータ) とは何ですか

リソースの無駄を避けるため、スレッドを使用中はオープンし、使用していない場合はスレッドをクローズするように頻繁に切り替えると、リソースの無駄が発生します。

スレッドプールの作成

package com.japhet.util;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {

    public static void main(String[] args) {
        // 定义线程池里面初始化10个线程
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        // 定义线程池里面就1个线程
        ExecutorService executorService2 = Executors.newSingleThreadExecutor();
        //定义线程池里面就n个线程,可扩容,根据你调用
        ExecutorService executorService3 = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 20; i++) {
                final int x = i;
//                executorService.execute(()->{
//                executorService2.execute(()->{
                executorService3.execute(()->{
                    System.out.println(x+"线程---"+Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
//            关闭
//            executorService.shutdown();
//            executorService2.shutdown();
            executorService3.shutdown();
        }
    }
}

(注: これら 3 つの方法を使用してスレッド プールを作成することは推奨されません。Alibaba Java 開発マニュアルには、これら 3 つの方法を使用してスレッドを作成すると記載されているためです。プールを使用すると、OOM メモリ オーバーフローが発生し、大量のリクエストが蓄積され、それらを処理する時間がなくなります。通常、自分でスレッド プールを作成し、7 つのパラメータを渡します。)

Java で作成されるスレッド プールの種類、スレッド プールが使用される理由、および拒否戦略について

スレッド プールを使用する理由: スレッド プールを使用すると、より多くのリソースが消費され、管理性が向上し、統合された割り当て、チューニング、監視が提供されるため、スレッドの作成と破棄を繰り返し行う必要がありません。

newSingleThreadPool、newFixedThreadPool、newCachedThreadPool、newScheduledThreadPool、newWorkStealingPool

内部パラメータ:

corePoolSize : スレッド プール内のコア スレッドの数を示します。スレッド プールが初期化されると、コア スレッドが作成されて待機状態になります。アイドル状態の場合、コア スレッドは破棄されないため、タスクが到着したときに新しいスレッドを作成する時間とパフォーマンスのオーバーヘッドが削減されます。

maximumPoolSize : スレッドの最大数を示します。これは、コア スレッドの数が使い果たされたことを意味します。この場合、新しいスレッドはタスクを実行するためにのみ再作成できますが、前提条件は、スレッドの最大数を超えられないことです。それ以外の場合、タスクはブロッキング キューに入り、スレッドがアイドル状態になるまでラインで待機するだけで、タスクの実行を続行できます。

keepAliveTime : スレッドの生存時間を示します。コア スレッドに加えて、新しく作成されたスレッドがどれくらいの時間生存できるかを示します。これは、これらの新しいスレッドがタスクを完了し、その後アイドル状態になると、一定期間後に破棄されることを意味します。

単位 : 生存時間の単位。

workQueue: タスクのブロッキング キューを表します。多くのタスクがあり、少数のスレッドしか存在しないため、まだ実行されていないタスクはキューに入れられます。キューは FIFO であることがわかります。スレッドがアイドル状態のときは、このようにしてタスクが取り出されます。通常、これを実装する必要はありません。

threadFactoy: 线程工厂

拒否戦略 (ハンドラー): ワークキューがいっぱいで、maximumPoolSize もいっぱいの場合、新しいスレッドは拒否される必要があります。一般的な拒否戦略は 4 つあります

ThreadPoolExecutor.AbortPolicy: タスクを破棄し、RejectedExecutionException をスローします。

ThreadPoolExecutor.DiscardPolicy: タスクを破棄しますが、例外はスローしません。

ThreadPoolExecutor.DiscardOldestPolicy: キューの最前部のタスクを破棄し、拒否されたタスクを再送信します

ThreadPoolExecutor.CallerRunsPolicy: タスクは呼び出しスレッド (タスクを送信したスレッド) によって処理されます。

カスタムスレッドプール

package com.japhet.util;

import java.util.concurrent.*;


//创建自定义线程池,
// 只能处理 循环里面的前8个,后面的12个就被拒绝策略给拒绝了
public class ThreadPoolDemo2 {

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
                5,
                2L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        try {
            for (int i = 0; i < 20; i++) {
                final int x = i;
                threadPoolExecutor.execute(()->{
                    System.out.println(x+"线程---"+Thread.currentThread().getName());
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            threadPoolExecutor.shutdown();
        }

    }
}

おすすめ

転載: blog.csdn.net/Japhet_jiu/article/details/129166010