JAVA進級シリーズ-同時プログラミング-第5編Thread API

8711 ワード

前編ではThreadクラスの構造方法を紹介しましたが、構造方法だけでは足りません.このクラスでよく使われるAPIをもっと勉強しなければなりません.そうすれば、このクラスをもっと深く理解することができ、同時により多くの選択をすることができます.
Threadクラスが提供するAPIは数十個あり,紙面の問題のため,本論文では代表的なものをいくつか選択して説明する.残りのAPIの仲間たちが興味を持っているのはソースコードで調べることができて、私に伝言を与えることができて、私たちは共同で勉強することを検討します.
ターゲット
  • currentThread
  • setPriority
  • yield
  • sleep
  • interrupt
  • interrupted
  • join

  • 内容
    1. currentThread
    この方法は、現在の実行スレッドの参照を返すために使用され、コードブロックで現在のスレッドオブジェクトを取得することができます.簡単に見えますが、非常に広く使用されており、後続のコンテンツで多く使用されます.
    方法:
    public static native Thread currentThread();

    コード:
    /**
     *            Thread.currentThread()                   。
     */
    public class CurrentThreadDemo {
        public static void main(String[] args) {
            //      :true
            Thread t = new Thread(() -> {
                System.out.println("t".equals(Thread.currentThread().getName()));
            }, "t");
            t.start();
            //      :true
            System.out.println("main".equals(Thread.currentThread().getName()));
        }
    }

    2. setPriority
    プロセスにはプロセスの優先度があり,スレッドにも優先度があり,理論的には優先度の高いスレッドはCPUによって優先的にスケジューリングされるが,実際には望ましくないことが多い.
    CPUが忙しい場合、優先度を設定するとより多くのCPUタイムスライスが得られる可能性がありますが、CPUが空いている場合、優先度を設定することはほとんど役に立ちません.したがって、プログラム設計で優先度を使用して特定のビジネスをバインドしたり、ビジネスを優先度に依存させたりしようとしないでください.この結果は、あなたが望む結果と一致しない可能性があります.
    方法:
    public final void setPriority(int newPriority);    //        
    public final int getPriority();    //        

    ケース:
    /**
     * t1         t2     ,             t2     
     *                 :
     *    t1: 59573
     *    t2: 34321
     *    CPU             ,             
     */
    public class PriorityDemo {
        public static void main(String[] args) {
            Thread t1 = new Thread(() -> {
                while (true) {
                    System.out.println("t1");
                }
            }, "t1");
            Thread t2 = new Thread(() -> {
                while (true) {
                    System.out.println("t2");
                }
            }, "t2");
            //     :1,    :5,    :10
            t1.setPriority(9);
            t2.setPriority(10);
    
            t1.start();
            t2.start();
        }
    }

    3. yield
    yieldメソッドは、スケジューラに現在のスレッドがプロセッサの現在の使用を放棄することを示す啓発的なメソッドに属します.スケジューラは、このプロンプトを無視できます.詳細なパフォーマンス分析とベンチマークテストと組み合わせて、実際に必要な効果があることを確認する必要があります.
    この方法はあまり使われていません.デバッグまたはテストの目的では、競合条件によるエラーの再現に役立つ可能性があります.
    方法:
    public static native void yield();

    ケース:
    /**
     *                           0    ,     1    
     *          ,         0    
     */
    public class YieldDemo {
        public static void main(String[] args) {
            IntStream.range(0, 2).mapToObj(YieldDemo::test).forEach(Thread::start);
        }
    
        private static Thread test(int index){
            return new Thread(() -> {
                // if (index == 0){
                //     Thread.yield();
                // }
                System.out.println(index);
            });
        }
    }

    4. sleep
    sleepは、システムタイマとスケジューラの精度と正確性に基づいて、現在実行中のスレッドを指定したミリ秒数までスリープ状態(一時的に実行を停止)にします.このスレッドはモニタの所有権を失うことはありません(例えばmonitorロック、monitorロックについては後述します).
    方法:
    public static native void sleep(long millis);    //       
    public static void sleep(long millis, int nanos);    //           

    ケース:
    /**
     *       ,                    ,           
     * Thread.sleep()                
     */
    public class SleepDemo {
        public static void main(String[] args) {
            new Thread(() -> {
                long startTime = System.currentTimeMillis();
                sleep(2000);
                System.out.printf("%s    :%d%s", Thread.currentThread().getName(), System.currentTimeMillis() - startTime, "ms");
                System.out.println("");
            }, "t").start();
    
            long startTime = System.currentTimeMillis();
            sleep(3000);
            System.out.printf("%s    :%d%s", Thread.currentThread().getName(), System.currentTimeMillis() - startTime, "ms");
        }
    
        private static void sleep(long millis) {
            try {
                Thread.sleep(millis);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    yieldとsleepの違い:
  • yieldはCPUスケジューラに対するヒントにすぎず、有効になるとスレッドコンテキストの切り替えを招く.
  • sleepは現在のスレッドが指定した時間を一時停止し、CPUタイムスライスの消費がない.
  • yieldはスレッドをRUNNING状態からRUNNABLE状態にする.
  • sleepはスレッドを短いblockにし、その後、所定の時間内にCPUリソースを解放する.
  • yieldは一定の有効性を保証できないが、sleepはほぼ100%所定の時間に従ってスリープする(最終スリープ時間はシステムのタイマとスケジューラの精度に準じる)
  • .
  • yieldは割り込み信号をキャプチャすることができず、sleepは別のスレッドによって割り込み信号
  • をキャプチャすることができる.
    5. interrupt
    スレッドinterruptは非常に重要なAPIであり、呼び出された場合、よく使用される方法でもあります.
  • Objectクラスのwait()、wait(long)またはwait(long,int)メソッドまたはjoin()、join(long)、join(long,int)メソッドの場合、このスレッドはブロックされ、このようなsleep(long)またはsleep(long,int)メソッドの場合、その中断状態はクリアされ、InterruptedExceptionが受信される.
  • InterruptibleChannelのI/Oが動作すると、チャネルは閉じられ、スレッドの割り込み状態が設定され、スレッドはjava.nio.channels.ClosedByInterruptExceptionを受け取る.
  • Selectorのwakeupメソッドでは、スレッドの割り込み状態が設定され、セレクタの起動方法を呼び出すように、選択操作からすぐに戻ります(ゼロ以外の値がある場合があります).
    これに関連するAPIはまだいくつかあります.
    方法:
    public void interrupt();    //     
    public static boolean interrupted();    //            ,              
    public boolean isInterrupted();    //            ,         
    // interrupted   isInterrupted            isInterrupted()    ,        ClearInterrupted              interrupt    ,       ,            false
    private native boolean isInterrupted(boolean ClearInterrupted);

    ケース:
    /**
     *        t,   1  
     *       2    ,    t    ,     :interrupt,    
     */
    public class InterruptDemo {
        public static void main(String[] args) throws InterruptedException {
            Thread t = new Thread(() -> {
                try {
                    Thread.sleep(60 * 1000);
                } catch (InterruptedException e) {
                    System.out.println("interrupt");
                }
            }, "t");
            t.start();
    
            Thread.sleep(2000);
            t.interrupt();
        }
    }

    スレッドの内部にはinterrupt flagという識別子が存在し、スレッドがinterruptである場合、そのflagが設定され、ソースコードにも対応する記述が見られる.
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();
    
        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

    しかし、現在のスレッドが割り込み可能なメソッドを実行している場合、interruptメソッドを呼び出して割り込み、逆にflagが明らかになることは、後述する.
    7. join
    Threadのjoin法も同様に非常に重要な方法であり,その特性を用いて多くの比較的強い機能を実現することができ,Threadクラスでは以下のように3つの異なる方法を提供した.
    方法:
    public final void join();    //              
    public final synchronized void join(long millis);    //          ,  0      
    public final synchronized void join(long millis, int nanos); //              ,  0      

    ケース:
    /**
     *        1   2      ,        。
     *   main            ,         1   2                  
     *   main         ,    join      ,            
     */
    public class JoinDemo {
        public static void main(String[] args) throws InterruptedException {
            List list = IntStream.range(1, 3).mapToObj(JoinDemo::getThread).collect(Collectors.toList());
    
            list.forEach(Thread::start);
    
            for (Thread thread : list) {
                thread.join();
            }
    
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "-" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private static Thread getThread(int name){
            return new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + "-" + i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, String.valueOf(name));
        }
    }

    まとめ
    この文章の中で、私達はThreadのいくつかの比較的によくあるAPIを学んで、ThreadのAPIは高い同時プログラミングの基礎を掌握するので、とても熟練して掌握する必要があります!
    今日の文章はここまで终わって、友达はどんな提案あるいは意见があって私に连络して改善してください、あなた达の支持は私の最大の动力です!!!
    本文はブロググループから1文多発などの運営ツールプラットフォームを配布する.
    OpenWriteリリース