スレッドの作成からスレッドプールの分析まで、2020年のJavaマルチスレッドインタビューの質問にはこれで十分です(回答付き)

序文

プロセスとは、メモリ内で実行されているアプリケーションプログラムを指します。各プロセスには、独自の独立したメモリスペース、つまりプロセススペースまたは(仮想スペース)があります。プロセスはスレッドに依存しませんが、独立して存在し、プロセス内で複数のスレッドを開始できます。
スレッドはプロセス内の実行フローを指し、複数のスレッドをプロセス内で実行できます。スレッドは常に特定のプロセスに属します。スレッドには独自の仮想アドレス空間がありません。プロセスに割り当てられたすべてのリソースをプロセス内の他のスレッドと共有します。統合プロセス内のスレッドはヒープメモリを共有し、各スレッドは独自のスタックメモリがあります。「同時」実行は人間の認識であり、実行は実際にはスレッド間でローテーションされます。

同期および非同期

同期:キューに入れられた実行、非効率的ですが安全です。
非同期:同期実行、高効率ですが安全でないデータ。

並行性と並列性

同時実行性:同じ期間に発生する2つ以上のイベントを指します。
並列:2つ以上のイベントが同時に(同時に)発生します。

スレッド作成の3つの方法

1.継承方法

public  class MyThread extends Thread{
    @Override
    public void run(){
    }
}

        MyThread m = new MyThread();
        m.start();

2.インターフェース方式

        //实现runnable
        //1 创建一个任务对象
        MyRunnable r = new MyRunnable();
        //创建一个线程并给他一个任务
        Thread t = new Thread(r);
        //启动线程
        t.start();

インターフェイスの利点:

スレッドの継承と比較して、Runnableの実装には次の利点があります
。1。タスクを作成してからスレッドにタスクを割り当てることでマルチスレッドが実現されます。これは、複数のスレッドが同時にタスクを実行する状況に適しています
。単一の継承によってもたらされる制限を回避し
ます。3。、タスクとスレッドが分離され、プログラムの堅牢性が向上します
。4 後で学習するスレッドプールテクノロジは、実行可能タイプのタスクを受け入れますが、スレッドタイプのスレッドは受け入れません。

    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("12345"+i);
                }
            }
        }.start();

ただし、スレッドを継承する非常に簡単な方法があります。匿名の内部クラスでrun()を書き換えることにより、新しいクラスを作成せずにマルチスレッドを簡単に実現できます。各スレッドには独自のスタックスペースがあり、ヒープメモリを共有します。
スレッドによって呼び出されたメソッド。メソッドはこのスレッドで実行されます。

3. Callableは、スレッドステータスの戻りを実現します(Callalbleインターフェイスを実装します)

Callalbleインターフェースは、実行結果の返送をサポートしています。実行結果を取得するには、FutureTask.get()を呼び出す必要があります。このメソッドは、メインプロセスの実行の継続を
ブロックします。呼び出されない場合、ブロックされません。

	Callable<Integer> callable= new MyCallable();
	FutureTask<Integer> future = new FutureTask<>(callable); 
	new Thread(future).start();
	Integer j=task.get();
	System.out.println("return"+j);

  1. Callableインターフェースを実装し、callメソッドを実装するクラスを作成します
class Mycallable implements Callable<T> { 
	@Override 
	public <T> call() throws Exception { 
	return T; 
	} 
} 

  1. FutureTaskオブジェクトを作成し、最初のステップで記述されたCallableクラスオブジェクトFutureTaskを渡しますfuture = new FutureTask <>(callable);
  2. スレッドを介して、スレッドを開始しますnew Thread(future).start();

getメソッドが呼び出されると、メインスレッドは実行が完了するのを待ってから実行されます。呼び出されない場合、メインスレッドには影響がなく、並列化できます。

FutureTask()メソッド

その親Future()メソッド

デーモンスレッドとユーザースレッド

スレッドはデーモンスレッドとユーザースレッドに分けられます。
ユーザースレッド:プロセスに存続するユーザースレッドが含まれていない場合、プロセスは終了します。
デーモンスレッド:ユーザースレッドを保護します。最後のユーザースレッドが終了すると、すべてのデーモンスレッドが自動的に終了します。
Javaのデフォルトスレッドはユーザースレッドであり、デーモンスレッドはスレッドでsetDaemon(true)を呼び出すことによって設定されます。

        Thread t1 = new Thread(new MyRunnable());
        //设置守护线程
        t1.setDaemon(true);
        t1.start();

スレッド同期の3つの方法

1.コードブロックを同期し、同じオブジェクトなどのロックを指定します

形式:同期(オブジェクトのロック){}

public class Demo8 {
    public static void main(String[] args) {
        Object o = new Object();
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        private Object o = new Object();
        @Override
        public void run() {
                while (true) {
                    synchronized (o) {
                        if (count > 0) {
                         //卖票
                            System.out.println("正在准备卖票");
                            try {
                            Thread.sleep(1000);
                            } catch (InterruptedException e) {
                            e.printStackTrace();
                            }
                            count--;
                            System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                        }else {
                            break;
                        }

                }
            }
        }
    }
}

2.同期方法

同期メソッドはrun()で呼び出されます。通常のメソッドの場合は、メソッド内のthis(このオブジェクト)をロックとして使用します。静的メソッドの場合は、クラス名.class(バイトコードファイルオブジェクト)です。

        public synchronized boolean test(){
                System.out.println("测试");
            }

3.ディスプレイロック

同期コードブロックと同期メソッドはどちらも暗黙的なロックです。
明示的なロックはより直感的で、オブジェクト指向の自己生成ロック、および自己ロックとロック解除を反映しています。

    static class Test implements Runnable{
        //总票数
        //参数为true表示公平锁    默认是false 不是公平锁
        private Lock l = new ReentrantLock(true);
        @Override
        public void run() {
            l.lock();
            System.out.println("测试");
            l.unlock();
        }
    }

フェアロックとアンフェアロック

フェアロック:先着順、一緒にキューに入れます。
不公平ロック:
デフォルトでは誰もがJavaを取得します。デフォルトでは不公平ロックです。フェアロックは、明示的ロックの構築方法によって実現できます。

//设置公平锁
private Lock l= new ReentrantLock(true);

パラメータが渡されない場合は公平ではなく、デフォルトではfalseですが、渡される場合は公平です

デッドロックの回避

ロックを引き起こす可能性のあるメソッドでは、ロックを引き起こして別のロックを生成する可能性のある別のメソッドを呼び出さないでください。
以下はデッドロックの例です。

public class Demo11 {
    public static void main(String[] args) {
        //线程死锁
        Culprit c = new Culprit();
        Police p = new Police();
        new MyThread(c,p).start();
        c.say(p);
    }

    static class MyThread extends Thread{
        private Culprit c;
        private Police p;
        MyThread(Culprit c,Police p){
            this.c = c;
            this.p = p;
        }

        @Override
        public void run() {
            p.say(c);
        }
    }
    static class Culprit{
        public synchronized void say(Police p){//两方法公用this(此对象)锁,第一个方法不执行完无法执行第二个方法
            System.out.println("罪犯:你放了我,我放了人质");
            p.fun();
        }
        public synchronized void fun(){
            System.out.println("罪犯被放了,罪犯也放了人质");
        }
    }
    static class Police{
        public synchronized void say(Culprit c){
            System.out.println("警察:你放了人质,我放了你");
            c.fun();
        }
        public synchronized void fun(){
            System.out.println("警察救了人质,但是罪犯跑了");
        }
    }
}

デッドロック

最初のスレッドがp.fun()を実行した場合(想定)、2番目のスレッドpはp.say()メソッドを実行しておらず、この時点ではロックに到達していません。両方のメソッドを実行できますが、エラーが発生します。

生産者と消費者

1.クラスを変更して読み取ることができます。スレッド同期を使用しない場合、変更中に別のスレッドによる読み取り操作が発生し、ダーティデータが読み取られます。
2.同期スレッド同期方式を追加することで、変更時に読み取れないことでダーティデータの読み取りの問題を解決できますが、2つのスレッド間の同期ができず、1つのスレッドのタイムスライスをプリエンプトする現象が発生します。同期できません。
3.読み取りと変更を行い、相互の同期を待機できる場合は、待機操作と通知操作も追加する必要があります。1つはnotifyallの実行後に休止して識別フラグを設定し、もう1つは識別フラグを読み取って実行します。目覚めた後、そして休んで、他を目覚めさせます。フラグフラグを追加して、スレッドの実行順序を決定します。

package com.java.demo;

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

public class Demo4  {

    /**
     * 多线程通信问题, 生产者与消费者问题
     * @param args
     */
    public static void main(String[] args) {
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }

    //厨师
    static class Cook extends Thread{
        private Food f;
        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for(int i=0;i<100;i++){
                if(i%2==0){
                    f.setNameAndSaste("老干妈小米粥","香辣味");
                }else{
                    f.setNameAndSaste("煎饼果子","甜辣味");
                }
            }
        }
    }
    //服务生
    static class Waiter extends Thread{
        private Food f;
        public Waiter(Food f) {
            this.f = f;
        }
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;
        private String taste;

        //true 表示可以生产
        private boolean flag = true;

        public synchronized void setNameAndSaste(String name,String taste){
            if(flag) {
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public synchronized void get(){
            if(!flag) {
                System.out.println("服务员端走的菜的名称是:" + name + ",味道:" + taste);
                flag = true;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

スレッドステータス

スレッドの状態。スレッドは、次のいずれかの状態になります。NEWによって開始されていないスレッド

、この状態にあります。Java仮想マシンで実行される
RUNNABLE
スレッドはこの状態です。
BLOCKED
は、この状態のモニターロックスレッドを待機してブロックされます。
WAITING
別のスレッドがこの状態で特定の操作のスレッドを実行するために無期限に待機しています。
TIMED_WAITING
指定された待機時間まで別のスレッドが操作を実行するのを待機しているスレッドはこの状態です。
TERMINATEDの
終了スレッドはこの状態です。
スレッドは、特定の時点で1つの状態にしかなれません。これらの状態は仮想マシンの状態であり、オペレーティングシステムのスレッドの状態を反映していません。

スレッドプール

1.キャッシュスレッドプール

無制限の長さ
タスクが追加された後の実行フロー:
1。スレッドプールにアイドルスレッドがあるかどうかを確認します
2.存在する場合は使用し
ます3.存在しない場合は、スレッドを作成して使用します


        ExecutorService service = Executors.newCachedThreadPool();
        //指挥线程池执行新的任务
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"测试");
            }

        });

2.固定長スレッドプール

長さは指定されたスレッドプール
です。タスクに参加した後の実行フロー:
1スレッドプールにアイドルスレッドがあるかどうかを確認します
2存在する場合は、を使用します
3アイドルスレッドがなく、スレッドプールがいっぱいでない場合は、作成します。スレッドをスレッドプールに入れて使用する
4アイドル状態のスレッドがなく、スレッドプールがいっぱいの場合は、スレッドプールのアイドル状態のスレッドを待ちます

		//设置定长线程池
        ExecutorService service = Executors.newFixedThreadPool(2);
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"测试");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });

3.シングルスレッドスレッドプール

効果は、長さが1の固定長スレッドプールと同じです。
実行プロセス
1スレッドプール内のスレッドがアイドル状態かどうかを判断します
2アイドル状態の場合は使用します
3アイドル状態でない場合は、アイドル状態になるのを待ってから使用します

        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"测试");
            }
        });

4.期間固定長スレッドプール

执行流程
    1 判断线程池是否存在空闲线程
    2 存在则使用
    3 不存在空闲线程  且线程池未满的情况下  则创建线程  并放入线程池中  然后使用
    4 不存在空闲线程  且线程池已满的情况下  则等待线程池的空闲线程

定期的なタスクの実行:
特定のタスクがトリガーされたときに、特定のタスクを自動的に実行します

1回実行:

        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        //定时执行一次
        //参数1:定时执行的任务
        //参数2:时长数字
        //参数3:2的时间单位    Timeunit的常量指定
       	scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"测试");
            }
        },5, TimeUnit.SECONDS);      //5秒钟后执行*/

定期的な実行

        周期性执行任务
            参数1:任务
            参数2:延迟时长数字(第一次在执行上面时间以后)
            参数3:周期时长数字(没隔多久执行一次)
            参数4:时长数字的单位
        * **/
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"测试");
            }
        },5,1,TimeUnit.SECONDS);

ラムダ式

匿名内部クラスをパラメーターとして使用する場合、ラムダ表記を使用してコードを大幅に簡略化できます。

        //冗余的Runnable编写方式
       Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("测试");
            }
        });
        t.start();

渡されたパラメータを保持し、メソッド本体を書き換えたままにし、->接続を途中で使用します

        Thread t = new Thread(() -> System.out.println("测试"));

総括する

最後に、すべての人のためにJavaアーキテクチャの学習資料を用意しました。学習テクノロジのコンテンツには、Spring、Dubbo、MyBatis、RPC、ソースコード分析、高並行性、高性能、分散、パフォーマンス最適化、マイクロサービスの高度なアーキテクチャ開発などが含まれます。困っている友達はここをクリックしてcsdnにコメントし、自分でダウンロードしてくださいもう1つの注意点として、完全なレビューは神経質な精神状態を取り除くための鍵ですが、十分にレビューすれば、当然、面接プロセスに自信が持てるようになります。

おすすめ

転載: blog.csdn.net/jiagouwgm/article/details/111676667