Java Advanced ---マルチスレッド

JavaAdvanced-マルチスレッド

1つは、スレッドの概念です。

スレッドについて話す前に、プロセスを何度も立ち上げる必要があります。(もう一度たくさんの単語を読んだので、もうわからないようです)

処理する:

プログラムの1回限りの実行。

糸:

オペレーティングシステムが操作スケジューリングを実行できる最小単位。これはプロセスに含まれ、プロセスの実際の操作単位です。

第二に、スレッドの実現と方法

Thread thread = new Thread("线程1");//线程创建
System.out.println(thread.getName());//获取线程名称
thread.start();			//线程启动
thread.setPriority(8);  //设置优先级 1~10
thread.getState();		//获取线程状态
thread.isAlive();		//查看线程是否运行

第三に、マルチスレッドの実現

マルチスレッドを実装する方法:1。Thread
クラスを継承し
ます2.Runnableインターフェイスを
実装します3.Callableインターフェイスを実装します
4.スレッドプール

1. Threadクラスを継承し
、runメソッド書き直します

public class MyThread extends Thread{
    public void run(){      //线程运行的主体
        for (int i = 1; i <= 20; i++) {
            System.out.println(i+".你好,来自线程"+Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        MyThread thread2 = new MyThread();

        thread.start();     //真正意义上的多线程,因为run还是main主线程调用
        thread2.start();

    }
}

2. Runnableインターフェースを実装し
、runメソッド書き直します

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 20; i++) {
            System.out.println(i+".你好,来自线程"+Thread.currentThread().getName());
            Thread.yield();     //礼让:放弃本次就会,重写竞争
        }
    }

    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        Thread thread = new Thread(mr,"子线程1");
        Thread thread2 = new Thread(mr,"子线程2");
        thread.setPriority(1);      //设置优先级
        thread.setPriority(10);

        thread.start();
        thread2.start();
    }
}

3. Callableインターフェイスを実装し
、callメソッド書き直します。run
メソッドには戻り値がないため、callメソッドを使用して戻り値をクライアントプログラムに返す必要があります。

Callableインターフェースを使用してマルチスレッドを実装する手順
1.最初の手順:Callableサブクラスのインスタンス化されたオブジェクトを作成する

2.ステップ2:FutureTaskオブジェクトを作成し、CallableオブジェクトをFutureTask構築メソッドに渡します(注:FutureTaskはRunnableインターフェイスとFutureインターフェイスを実装します)

3. 3番目のステップ:Threadオブジェクトをインスタンス化し、コンストラクターでFurureTaskオブジェクトを渡します

4.ステップ4:スレッドを開始します

FutureTask ft = new FutureTask(Callableインターフェイスの実装クラス)
Thread thread = new Thread(ft
);
thread.start(); ft.get(); // call()メソッドの戻り値を取得します

public class MyCallable implements Callable {
    @Override
    public String call() throws Exception {
        System.out.println("执行call方法");
        return "999";
    }

    public static void main(String[] args) {
        FutureTask<String> ft = new FutureTask(new MyCallable());
        Thread thread1 = new Thread(ft);
        thread1.start();
    }
}

4、スレッドプール
1。newFixedThreadPool
固定サイズのスレッドプール。スレッドプールのサイズを指定できます。スレッドプールのcorePoolSizeとmaximumPoolSizeは等しく、ブロッキングキューはLinkedBlockingQueueを使用し、サイズは最大整数です。

スレッドプール内のスレッド数は常に同じです。新しいタスクが送信されると、スレッドプール内のアイドル状態のスレッドがすぐに実行されます。そうでない場合は、一時的にブロックキューに保存されます。固定サイズのスレッドプールの場合、スレッド数に変更はありません。同時に、無制限のLinkedBlockingQueueは、実行されたタスクを格納するために使用されます。タスクが非常に頻繁に送信される場合、LinkedBlockingQueue

急激な増加、システムリソースの枯渇の問題があります。また、スレッドプールがアイドル状態の場合、つまりスレッドプールに実行可能なタスクがない場合、ワーカースレッドは解放されず、特定のシステムリソースを占有するため、シャットダウンが必要になります。

public class FixPoolDemo {

    private static Runnable getThread(final int i) {
        return new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i);
            }
        };
    }

    public static void main(String args[]) {
        ExecutorService fixPool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            fixPool.execute(getThread(i));
        }
        fixPool.shutdown();
    }
}

2. newSingleThreadExecutor
シングルスレッドスレッドプールスレッドスレッドプールは1つだけで、ブロッキングキューはLinkedBlockingQueueを使用します。追加のタスクがスレッドプールに送信されると、それらは一時的にブロッキングキューに保存され、空いたときに実行されます。タスクは先入れ先出しの順序で実行されます。

public class CachePool {
    private static Runnable getThread(final int i){
        return new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                }catch (Exception e){

                }
                System.out.println(i);
            }
        };
    }

    public static  void main(String args[]){
        ExecutorService cachePool = Executors.newCachedThreadPool();
        for (int i=1;i<=10;i++){
            cachePool.execute(getThread(i));
        }
    }
}

3. newCachedThreadPool
キャッシュスレッドプール。キャッシュされたスレッドは、デフォルトで60秒間存続します。スレッドのコアプールcorePoolSizeのサイズは0、最大コアプールはInteger.MAX_VALUE、ブロッキングキューはSynchronousQueueを使用します。これは直接送信されるブロッキングキューであり、常にスレッドプールに新しいスレッドを追加して新しいタスクを実行するように強制します。タスクの実行がない場合、スレッドのアイドル時間がkeepAliveTime(60秒)を超えると、ワーカースレッドは終了してリサイクルされます。新しいタスクが送信されると、アイドルスレッドがない場合は、新しいスレッドが作成されます。タスクを実行します。これにより、特定のシステムオーバーヘッドが発生します。多数のタスクが同時に送信され、タスクの実行時間がそれほど速くない場合、スレッドプールは同量のスレッドプール処理タスクを追加し、システムリソースをすぐに使い果たしてしまう可能性があります。

public class SingPoolDemo {
    private static Runnable getThread(final int i){
        return new Runnable() {
            @Override
            public void run() {
                try {

                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i);
            }
        };
    }

    public static void main(String args[]) throws InterruptedException {
        ExecutorService singPool = Executors.newSingleThreadExecutor();
        for (int i=0;i<10;i++){
            singPool.execute(getThread(i));
        }
        singPool.shutdown();
    }

4. newScheduledThreadPool
タイミングスレッドプール。スレッドプールを使用して、タスクを定期的に実行できます。通常は、データを定期的に同期します。
scheduleAtFixedRate:タスクは固定頻度で実行され、期間は各タスクが正常に実行されるまでの間隔を指します。
schedultWithFixedDelay:タスクは固定遅延で実行されます。遅延とは、最後の実行が成功してから次の実行が開始されるまでの時間を指します。

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

        ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
        ses.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(4000);
                    System.out.println(Thread.currentThread().getId() + "执行了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 0, 2, TimeUnit.SECONDS);
    }
}

3、同期メカニズム

同期

例えば:

チケットの購入:
非同期

public class BuyTicket extends Thread{
    int left = 15;  //总共15张票,余票
    int used = 0;   //已卖票数

    public synchronized void buy(){
        
    }
    public void run(){
        //取票
        while (left>0){
            left--;
            used++;
            try {
                Thread.sleep(100);      //模拟网络延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.print(Thread.currentThread().getName()+"成功购买");
            System.out.println("当前已卖"+used+"张"+"余票数:"+left);
        }
    }

    public static void main(String[] args) {
        BuyTicket bt = new BuyTicket();
        Thread t1 = new Thread(bt,"奥网城");
        Thread t2 = new Thread(bt,"ssssssssssss");
        Thread t3 = new Thread(bt,"Tom");

        t1.start();
        t2.start();
        t3.start();
    }
}

【結果:同じチケットを複数人で購入する】

同期する

public class BuyTicket extends Thread{
    int left = 15;  //总共15张票,余票
    int used = 0;   //已卖票数

    public synchronized boolean buy(){
        if(left<=0)return false;
        left--;
        used++;
        System.out.print(Thread.currentThread().getName()+"成功购买");
        System.out.println(",当前已卖"+used+"张"+",余票数:"+left);
        return true;
    }

    public void run(){
        //取票
        while (true){
            if(!buy())
                break;
            try {
                Thread.sleep(100);  //网络延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

結果は次のことを示しています
ここに画像の説明を挿入

1.同期とロックの使用法の違い

同期:同期が必要なオブジェクトにこのコントロールを追加します。同期は、メソッドまたは特定のコードブロックに追加できます。括弧は、ロックが必要なオブジェクトを示します。

ロック:通常、ReentrantLockクラスをロックとして使用します。ロックとロック解除のポイントは、lock()とunlock()の表示で指摘する必要があります。したがって、unlock()は通常、デッドロックを防ぐためにfinallyブロックに書き込まれます。

2.同期とロックのパフォーマンスの違い

Synchronizedは実行のためにJVMによって管理され、
lockはjavaで記述されたロックを制御するコードです。

Java 1.5では、同期は非効率的です。これは重い操作であるため、操作インターフェイスを呼び出す必要があります。これにより、ロック以外の操作よりもシステム時間が長くなる可能性があります。対照的に、Javaが提供するLockオブジェクトを使用すると、パフォーマンスが向上します。

しかし、Java 1.6では、変更が加えられました。同期はセマンティクスが非常に明確であり、適応スピン、ロック除去、ロック粗大化、軽量ロック、バイアスロックなどを含め、多くの最適化が可能です。その結果、Java1.6での同期のパフォーマンスはLockのパフォーマンスより悪くはありません。関係者はまた、同期をさらにサポートしており、将来のバージョンでは最適化の余地があると述べました。

2つのメカニズムの具体的な違い:
Synchronizedは元々CPUペシミスティックロックメカニズムを使用していました。つまり、スレッドは排他ロックを取得しました。排他的ロックとは、他のスレッドがブロックにのみ依存して、スレッドがロックを解放するのを待つことを意味します。CPU変換スレッドがブロックされると、スレッドのコンテキスト切り替えが発生します。ロックをめぐって競合するスレッドが多いと、CPUのコンテキスト切り替えが頻繁に発生し、効率が低下します。

また、ロックは楽観的ロック方式を使用します。いわゆる楽観的ロックとは、毎回ロックせずに、競合がないと仮定して操作を完了することです。競合が原因で失敗した場合は、成功するまで再試行します。楽観的ロックのメカニズムは、CAS操作(コンペアアンドスワップ)です。ReentrantLockのソースコードをさらに調べると、ロックを取得するためのより重要なメソッドの1つがcompareAndSetStateであることがわかります。これは実際には、呼び出されたCPUによって提供される特別な命令です。

3.同期使用とロック使用の違い

一般に、同期されたプリミティブとReentrantLockの間に違いはありませんが、非常に複雑な同期アプリケーションでは、特に次の2つの要件が発生した場合は、ReentrantLockの使用を検討してください。

1.ロックの制御を待機している間にスレッドを中断する必要があります
。2。一部の待機通知を個別に処理する必要があります。ReentrantLockのConditionアプリケーションは、通知するスレッドを制御できます。3
。フェアロック機能を使用すると、各着信スレッドはキューに入れられます

おすすめ

転載: blog.csdn.net/qq_43288259/article/details/113548253