JUCシリーズ1-基礎知識

7802 ワード

JUCシリーズ-基礎知識
  • スレッド起動
  • コード例
  • Thread類
  • を継承する。
  • Runnableインターフェース
  • を実現する。
  • FutureTask
  • を利用する。
  • スレッドの一般的な方法
  • スレッド通知と待ち時間
  • wait方法
  • notify方法
  • 生産者と消費者コード例
  • 常用Thread類方法
  • join方法
  • interrupt方法
  • sleep方法
  • yield方法
  • ガードスレッドとユーザスレッド
  • スレッド起動
    通常起動には三つの方法があります。スレッドを起動します。2、Runnableインターフェースを実現し、run()方法を書き換え、スレッド構造として伝わることを実現し、スレッドを起動する。3、RunnableまたはCallableをFutureTaskで包装し、スレッド実行結果に戻すことができます。
    コードの例
    Thread類を継承する
    class MyThread extends Thread {
    
       public MyThread(String name) {
            super(name);
        }
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
     }
    
    public static void main(String[] args){
    	Thread t1 = new MyThread("test");
        t1.start();
    }
    
    Runnableインターフェースを実現
    public static void main(String[] args){
    	Runnable r = () -> System.out.println(Thread.currentThread().getName());
        Thread aThread = new Thread(r, "a");
        aThread.start();
    }
    
    FutureTaskを利用する
    1、Futureインターフェースは非同期実行の結果を表すために使用され、その主な方法は以下のように定義されている。V get()は実行結果を取得し、実行が終了しないとブロックされる。V get(long timeout,TimeUnit unit)は実行結果を取得し、実行が終了しないと指定された時間がブロックされます。bollan cancelは現在のスレッドタスクをキャンセルします。パラメータはtrueです。タスクを実行しているスレッドをキャンセルし、falseはタスクを実行しているスレッドをキャンセルしないことを表します。戻り値はキャンセル結果で、通常はジョブが完了しました。この方法を呼び出したらfalseに戻ります。bolean isdone()がtrueに戻るとスレッドが完了し、完了したシーンは正常終了、異常またはキャンセルなどを含む。bollan isCancelled()はtrueに戻ります。スレッドが正常に完成する前にキャンセルされます。
    2、Callableインターフェースは、Runnableに似ていて、一つのcall()方法だけを定義し、戻り値があります。JUCで非同期実行結果を取得する必要がある場合は、通常、このインターフェースを使用して実装される。
    3、RunnableFutureはRunnableとFutureを同時に実現するインターフェースです。FutureTaskは、RunnableFutureの実装クラスであり、主な方法は、run()が非同期タスクを実行し、get()がタスク結果を取得することである。
    4、FutureTaskタスクの結果を取得する定義インターフェースはFutureから来て、具体的な機能はCallableインターフェースから実現して、その内部はCallableオブジェクトを持ち、run()が非同期タスクを実行する時、実際にCallableオブジェクトのcall()方法を実行し、結果をoutcomeフィールドに保存します。get()メソッドを呼び出すと、スレッドの状態が判断されますが、まだ完了していないと、WaitNode変数は待ちスレッドのキューを表します。run()メソッドでタスクが完了すると、finish Complection()が取得結果を待つスレッドを起動します。
    public static void main(String[] args){
    	Callable callable = () -> {
                int sum = 0;
                for (int i = 1; i < 20; i++) {
                    sum += i;
                }
                System.out.println(Thread.currentThread().getName());
                return sum;
            };
        FutureTask futureTask = new FutureTask<>(callable);
        Thread callThread = new Thread(futureTask, "call");
        callThread.start();
        System.out.println("sum: " + futureTask.get());//future.get()     ,            
    }
    
    スレッドの使い方
    スレッド通知と待ち時間
    主にwait()、notify()、notifyAll()の方法を含んで、Object種類の中の方法に属します。
    waitの方法
    スレッドが変数を共有するwait()メソッドを呼び出すと、次のいくつかのイベントの一つが発生してから戻ってきます。(1)他のスレッドが共有対象のnotify()またはnotifyAll()メソッドを呼び出しました。2)他のスレッドは、Interrupt edExceptionが異常に戻ってくるスレッドを呼び出した。
    また、wait()メソッドを呼び出したスレッドが、そのオブジェクトのモニタロックを事前に取得していない場合、wait()メソッドを呼び出したときに、スレッドがIllegel MonitonterStation異常に投げ出されます。
    では、スレッドはどうやって共有変数のモニタロックを取得できますか?1)synchronized同期コードブロックを実行する場合、その共有変数をパラメータとして使用します。
    synchronized(    ){
    	so something
    }
    
    (2)この共有変数の方法を呼び出し、この方法はsynchronized修飾を使用している。
    synchronized void add(int a, int b){
    	so something
    }
    
    notifyメソッド
    共有対象のnotify()メソッドを呼び出したスレッドは、その共有変数上でwaitシリーズのメソッドを呼び出した後にマウントされたスレッドを起動します。共有変数に複数のスレッドが待機している可能性があります。どの待ちスレッドがランダムで起動されますか?
    また、呼び覚まされたスレッドは、すぐにwaitメソッドから戻って実行できません。共有対象のモニターロックを取得した後に、リターンする必要があります。つまり、呼び覚ましたスレッドは共有変数上のモニタロックを開放した後、呼び覚まされたスレッドも必ずしも共有対象のモニターロックを取得するとは限りません。これはスレッドが他のスレッドと競合しているためです。共有変数のモニタロックに競合するスレッドだけが実行されます。
    waitシリーズのように、現在のスレッドだけが共有変数のモニタロックを取得した後、共有変数のnotify()メソッドを呼び出すことができます。このような異常を避けるために、waitとnotifyシリーズの方法は、一般的に共有コードブロックで呼び出される。
    notifyAll()メソッドは、この共有変数上でwaitシリーズ方式を起動することにより、すべてのスレッドを起動します。
    生産者と消費者コードの例
    public static void main(String[] args){
    		ArrayDeque deque = new ArrayDeque<>(20);
            Runnable producer = () -> {
                while (true) {
                    synchronized (deque) {
                   	    //    while          
                        while (deque.size() == 20) {
                            try {
                                System.out.println("producer wait");
                                deque.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        Random r = new Random();
                        deque.add(r.nextInt(100));
                        deque.notifyAll(); //         ,         
                    }
                }
            };
    
            Runnable consumer = () -> {
                while (true) {
                    synchronized (deque) {
                        while (deque.size() == 0) {
                            try {
                                System.out.println("consumer wait");
                                deque.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("deque remove:" + deque.remove());
                        deque.notifyAll();
                    }
                }
            };
            new Thread(producer).start();
            new Thread(consumer).start();
            Thread.sleep(5000L);
     }
    
    常用Thread類の方法
    ジョンメソッド
    joinは無参であり、戻り値がvoidである方法であり、あるスレッドの実行が完了するのを待ってから、下に向かって実行する。
    スレッドAがスレッドBのジョインメソッドを起動するとブロックされ、他のスレッドがスレッドAのinterrupt()を起動してスレッドAを中断した場合、スレッドAはInterrupteExceptionをスローして戻ってきます。
    public static void main(String[] args){
    		Thread mainTread = Thread.currentThread();
            Runnable r1 = () -> {
                while (true) {
                }
            };
            Runnable r2 = () -> {
                try {
                    Thread.sleep(3000L);
                    mainTread.interrupt(); //           
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
            Thread thread1 = new Thread(r1);
            Thread thread2 = new Thread(r2);
            thread1.start();
            thread2.start();
    
            try {
                thread1.join(); //    1  ,     
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("thread is interrupted");
            }
    }
    
    interrupt方法
    スレッドの中断ビット状態はtrueであり、スレッドがすぐに停止することを意味しないが、中断されたスレッドは中断状態によって処理される。通常、スレッドがwait()、join()、sleep()を呼び出してふさがっている場合、この方法を起動すると、システムがInterrupteExceptionを投げます。
    Streadオブジェクト.isInterrupted()は対象方法で、あるスレッドの中断フラグビット状態に戻ります。
    Thread.interrupted()は静的な方法で、現在のスレッドの中断フラグビット状態を返しながら、フラグビットをクリアします。
    sleepメソッド
    Thread類には、実行中のスレッドがThreadのsleepメソッドを呼び出した後、指定された時間の実行権を一時的に渡す静的なsleep方法があります。つまり、この期間はCPUのスケジュールには参加しませんが、このスレッドが持っているモニタリソースは、ロックのようなものよりも譲れません。
    指定された睡眠時間が来たら、この関数は正常に戻ります。スレッドは準備状態になり、CPUのスケジュールに参加して、CPUリソースを取得したら、そのまま実行できます。スリープ中に他のスレッドがスレッドを呼び出した場合、このスレッドが中断された場合、スレッドはスリープメソッドを呼び出したところにIntermptedExceptionを投げて戻ってきます。
    waitメソッドはスレッドを掛けて同時にモニタの資源を譲ることができて、notifyを必要として目を覚まして、これはsleepの主要な違いです。
    yieldの方法
    Threadクラスには静的なyield方法があります。一つのスレッドがyieldメソッドを起動すると、実際にはスレッドスケジューラの現在のスレッド要求を暗示して、自分のCPUに使用させますが、スレッドスケジューラは無条件にこの暗示を無視できます。
    スレッドがyieldメソッドを起動すると、現在のスレッドはCPUの使用権を譲って、そして準備が整った状態になります。スレッドスケジューラはスレッドレディの中からスレッド優先度が一番高いスレッドを取得します。もちろん、CPUを譲ったばかりのスレッドにスケジュールしてCPU実行権を取得することも可能です。
    守護スレッドとユーザスレッド
    Javaのスレッドは、daemenスレッド(守護スレッド)とuserスレッド(ユーザスレッド)の2種類に分けられている。JVMの起動時にはmain関数を呼び出します。main関数のあるお金の流れはユーザースレッドです。実はJVM内部で同時に多くの保護スレッドが起動しました。例えばゴミ回収スレッドです。では、守護スレッドとユーザースレッドの違いは何ですか?違いの一つは、最後の非ガードスレッドが終了すると、JVMは正常に終了します。現在、ガードスレッドがあるかどうかは関係なく、つまり、ガードスレッドが終了するかどうかはJVMの脱退に影響しません。ところで、ユーザースレッドが終了していない限り、通常のJVMは終了しません。