Javaマルチスレッドではできない面接問題

47381 ワード

目次
  • 1.はじめに
  • 2.本文
  • 2.1 Threadクラスのstart()メソッドとrun()メソッドの違いは何ですか?
  • 2.2スレッドの実行順序の制御方法
  • 2.3マルチスレッドでの並列と同時の区別方法
  • 2.4 JavaでCPUを指定してスレッドを実行できますか?
  • 2.5プロジェクト開発において、Javaスレッドの優先度を考慮しますか?
  • 2.6 sleep()メソッドとwait()メソッドの違いは何ですか?
  • 2.7 Javaでスレッドの実行をどのように中断しますか?
  • 2.8現在のスレッドにCPUの実行権をどのように譲るか.
  • 2.9 sleep()、wait()、いったいその関数が割り込みマークをクリアするのか?
  • 3.最後
  • 参考
  • 1.はじめに
    マルチスレッド関連の面接問題は基礎的ですが、高周波面点です.
    ここで1篇の文章を書いてまとめて、結局、“紙の上で結局浅く感じなければならなくて、絶対にこの事を知ってお辞儀をします”.
    2.本文
    2.1 Threadクラスのstart()メソッドとrun()メソッドの違いは何ですか?
    所属違い:start()方法はThread類が自ら宣言する方法、run()方法はThread類がRunnableインターフェースから実現したもの位置決めの違い:Threadオブジェクトで呼び出すstart()メソッドがスレッドを作成して開くことができ、start()メソッドが呼び出されたからこそ、スレッドは無から有へ、有から起動へとなる.start()メソッド内部で呼び出されるstart0()これnativeメソッド.run()メソッドは実行する必要があるコードをカプセル化しただけで、これは一般的なメソッドにもある機能です.呼び出しの違い:スレッドを開くときstart()メソッドは手動で呼び出す必要がありますがrun()メソッドは仮想マシンで呼び出されます.
    2.2スレッドの実行順序をどのように制御しますか?
    プレゼンテーションコードを参照してください.
    class Task1 implements Runnable {
        public Task1() {
        }
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " is doing task.");
        }
    }
    
    public class Demo {
        public static void main(String[] args) {
            for (int i = 0; i < 5; i++) {
                Thread thread = new Thread(new Task1(), "Thread " + i);
                thread.start();
            }
            System.out.println(Thread.currentThread().getName() + " is doing task.");
        }
    }
    

    実行して、実行結果を確認します.
    main is doing task.
    Thread 1 is doing task.
    Thread 0 is doing task.
    Thread 2 is doing task.
    Thread 4 is doing task.
    Thread 3 is doing task.
    

    複数回実行すると、スレッドの実行順序が一致しません.Threadクラスのjoin()メソッドを使用してスレッドの実行順序を制御できます.
    class Task2 implements Runnable {
        private Thread joiner;
        public Task2(Thread joiner) {
            this.joiner = joiner;
        }
        @Override
        public void run() {
            try {
                joiner.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " is doing task.");
        }
    }
    public class JoinDemo {
        public static void main(String[] args) {
            Thread joiner = Thread.currentThread();
            for (int i = 0; i < 5; i++) {
                Thread thread = new Thread(new Task2(joiner), "Thread " + i);
                thread.start();
                joiner = thread;
            }
            System.out.println(Thread.currentThread().getName() + " is doing task.");
        }
    }
    

    同じ情報を印刷できるプログラムを複数回実行します.
    main is doing task.
    Thread 0 is doing task.
    Thread 1 is doing task.
    Thread 2 is doing task.
    Thread 3 is doing task.
    Thread 4 is doing task.
    
    join()メソッドの意味はthread2で呼び出されるthread1.join()thread2待たなければならないthread1運転完了後に続行するthread2の運転.なお、呼び出しthread1.join()メソッドは、thread2run()メソッドに書かなければならないものではありません.次の例を見てください.
    class Task3 implements Runnable {
        public Task3() {
        }
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " is doing task.");
        }
    }
    
    public class JoinDemo2 {
        public static void main(String[] args) {
            for (int i = 0; i < 5; i++) {
                Thread thread = new Thread(new Task3(), "Thread " + i);
                thread.start();
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + " is doing task.");
        }
    }
    

    印刷結果:
    Thread 0 is doing task.
    Thread 1 is doing task.
    Thread 2 is doing task.
    Thread 3 is doing task.
    Thread 4 is doing task.
    main is doing task.
    

    2.3マルチスレッド内の並列と同時をどのように区別しますか?
    パラレル(Parallel)は「並列実行」または「同時実行」であり、ある時点で、コンピュータオペレーティングシステムでは、1組のプログラムが独立した非同期の速度で実行され、ミクロでもマクロでもプログラムが一緒に実行されることを指す.コンカレント(Concurrent)とは、1つの時間帯にいくつかのプログラムが起動して実行されるまでの間にあり、これらのプログラムは同じプロセッサ上で実行されますが、いずれの時点でも1つのプログラムだけがプロセッサ上で実行されます.
    簡単に言えば、
    同時、複数の任務が、同じ時間帯に発生したということです.並行して、複数のタスクが、同じ時点で発生したということです.
    2.4 JavaでCPUを指定してスレッドを実行できますか?
    いいえ、Javaはできません.唯一介入できるのはC言語がカーネルのAPIを呼び出して指定することです.
    CPUは,オペレーティングシステムのマルチスレッドに対応してシステムの計算効率を向上させるためである.しかし、各カーネルにタスクを具体的に割り当てるのはJAVAとJVMではなくオペレーティングシステムです.つまり、あなたが実行しているマルチスレッドは、同じCPUカーネルに割り当てられて実行される可能性があります.異なるCPUで動作しない場合もあります.CPUの割り当てを制御できれば、オペレーティングシステムのapiで実現できるはずです.
    2.5プロジェクト開発の過程で、Javaスレッドの優先度を考慮しますか?
    Javaスレッドの優先度を考慮しますが、Javaスレッドの優先度には依存しません.
    優先度の低いスレッドは、実行頻度が低いだけで、優先度の低いスレッドが優先度の高いスレッドよりも必ず実行されるわけではありません.
    Javaでは、優先度が10個あります.
    /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;
    /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;
    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;
    

    しかし、多くのオペレーティングシステムとうまくマッピングできません.たとえば、Windowsには7つの優先度があり、固定されていません.信頼できるのは、優先度を調整するときに、MAX_PRIORITYNORM_PRIORITYMIN_PRIORITYの3つのレベルを使うだけです.
    2.6 sleep()メソッドとwait()メソッドの違いは何ですか?
  • 所属違い:sleep()はいThreadクラスの静的メソッド、wait()はいObjectクラスのメソッド
    // Thread   
    public static native void sleep(long millis) throws InterruptedException;
    // Object   
    public final native void wait(long timeout) throws InterruptedException;
    
    public final void wait() throws InterruptedException {
        wait(0);
    }
    
  • 同期中、CPUの実行権とロックの処理が異なる:wait()方法CPUの実行権を解放し、ロックを解放する.sleep()方法CPU実行権を解放し、ロックを解除しない.
  • 呼び出しの場合異なる:sleep()メソッドはどこでも呼び出すことができる;wait()メソッドは同期でのみ呼び出すことができ、同期で呼び出さないと投げ出されるIllegalMonitorStateException.

  • 2.7 Javaでは、スレッドの実行をどのように中断しますか?Threadstop()メソッドは使用しないでください.このメソッドは@Deprecatedと表記されています.公式にはThreadinterrupt()メソッドを使用して割り込み信号を出すことをお勧めします.これはコラボレーション式の割り込みです.
    次のデモコードを見てください.
    public class UseInterrupt2 {
        private static class MyThread extends Thread {
            @Override
            public void run() {
                while (!isInterrupted()) {
                    System.out.println(Thread.currentThread().getName() + " is running, " +
                            "isInterrupted() = " + isInterrupted());
                }
                System.out.println(Thread.currentThread().getName() + " end, " +
                        "isInterrupted() = " + isInterrupted());
            }
        }
    
        public static void main(String[] args) {
            MyThread myThread = new MyThread();
            myThread.start();
    
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //   main    ,   myThread   interrupt()   ,     myThread      
            myThread.interrupt();
        }
    }
    

    印刷結果:
    Thread-0 is running, isInterrupted() = false
    Thread-0 is running, isInterrupted() = false
    Thread-0 is running, isInterrupted() = false
    Thread-0 is running, isInterrupted() = false
    Thread-0 end, isInterrupted() = true
    

    2.8現在のスレッドにCPUの実行権をどのように譲るか.
    呼び出しThread.yield()メソッド:
    public static native void yield();
    

    なお、このメソッドを呼び出すのはスケジューラに信号を送るだけであり、現在のスレッドはCPUの実行権を譲りたいと考えている.しかし、スケジューラはこの信号を無視することができます.
    2.9 sleep()、wait()、いったいその関数は割り込みマークをクリアしますか?sleep()メソッドは割り込みフラグをクリアする:
    public class UseInterrupt3 {
        private static class MyThread extends Thread {
            @Override
            public void run() {
                while (!isInterrupted()) {
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        System.out.println(Thread.currentThread().getName() + " sleep interrupted, " +
                                "isInterrupted() = " + isInterrupted());
                    }
                    System.out.println(Thread.currentThread().getName() + " is running, " +
                            "isInterrupted() = " + isInterrupted());
                }
                System.out.println(Thread.currentThread().getName() + " end, " +
                        "isInterrupted() = " + isInterrupted());
            }
        }
    
        public static void main(String[] args) {
            MyThread myThread = new MyThread();
            myThread.start();
    
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //   main    ,   myThread   interrupt()   ,     myThread      
            myThread.interrupt();
        }
    }
    

    実行中は常に印刷されます.
    java.lang.InterruptedException: sleep interrupted
    	at java.lang.Thread.sleep(Native Method)
    	at com.java.advanced.features.concurrent.stopthread.interrupt.UseInterrupt3$MyThread.run(UseInterrupt3.java:9)
    Thread-0 sleep interrupted, isInterrupted() = false
    Thread-0 is running, isInterrupted() = false
    Thread-0 is running, isInterrupted() = false
    Thread-0 is running, isInterrupted() = false
    Thread-0 is running, isInterrupted() = false
    Thread-0 is running, isInterrupted() = false
    ...//         Thread-0 is running, isInterrupted() = false
    
    wait()方法でも割り込みフラグがクリアされる:
    public class UseInterrupt4 {
        private static class MyThread extends Thread {
            private Object lock = new Object();
            @Override
            public  void run() {
                synchronized (lock) {
                    while (!isInterrupted()) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            System.out.println(Thread.currentThread().getName() + " wait interrupted, " +
                                    "isInterrupted() = " + isInterrupted());
                        }
                        System.out.println(Thread.currentThread().getName() + " is running, " +
                                "isInterrupted() = " + isInterrupted());
                    }
                    System.out.println(Thread.currentThread().getName() + " end, " +
                            "isInterrupted() = " + isInterrupted());
                }
    
            }
        }
    
        public static void main(String[] args) {
            MyThread myThread = new MyThread();
            myThread.start();
    
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //   main    ,   myThread   interrupt()   ,     myThread      
            myThread.interrupt();
        }
    }
    

    印刷ログ:
    java.lang.InterruptedException
    	at java.lang.Object.wait(Native Method)
    	at java.lang.Object.wait(Object.java:502)
    	at com.java.advanced.features.concurrent.stopthread.interrupt.UseInterrupt4$MyThread.run(UseInterrupt4.java:11)
    Thread-0 wait interrupted, isInterrupted() = false
    Thread-0 is running, isInterrupted() = false
    

    3.最後に
    本文は学生たちのマルチスレッドに対する学習を深めることを望んでいる.
    リファレンス
  • 並行と並行の違い
  • 『Javaプログラミング思想』
  • javaスレッド割込みのいくつかの点についてsleep()やwait()などJDK内蔵のメソッド割込み異常を投げてスレッドの割込み状態をクリアする