JAVAスレッド-スレッドの状態はどうなりますか?どうやって働いていますか?


スレッドは同時プログラムの基礎であり、プログラム実行の最小単位でもあります。プロセスに依存して存在します。
一つのプロセスには複数のスレッドが含まれてもよく、マルチスレッドは一つのメモリ空間と一つのシステムリソースのセットを共有してもよいので、スレッド間の切り替えは資源をより節約し、より軽量化し、したがって軽量化プロセスと呼ばれる。
スレッドの状態はJDK 1.5の後に列挙で定義されています。Threadのソースコードには、以下の6つの状態が含まれています。
  • NEWは、新しい状態でスレッドが作成されましたが、まだ起動されていない時のスレッド状態です。
  • RUNNALE、準備状態、実行可能なスレッド状態を示し、実行中か、または待ち行列しているオペレーティングシステムがCPUリソースを割り当てます。
  • BLOCKEDは、閉塞待ちロックのスレッド状態を表し、閉塞状態にあるスレッドが、synchronizedコードブロックの実行を待つか、またはsynchronizedマークを使用する方法などのモニタロックを待っていることを示している。
  • WAITINGは、待ち状態にあり、待ち状態にあるスレッドが、他のスレッドがObject.wait()方法を起動するなど、特定の動作を実行するのを待っています。
  • TIMED_WAITINGは、時間待ち状態、待ち状態(WAITING)と同様に、タイムアウト時間設定の方法Object.waitやThread.joinなどを呼び出した場合にのみこの状態に入ります。
  • TEMINATEDは、スレッドの実行が完了したことを示す終了状態である。
  • スレッド状態のソースコードは以下の通りです。
    
    public enum State {
     /**
      *     ,       ,           
      */
     NEW,
     /**
      *     ,           ,               CPU   
      */
     RUNNABLE,
     /**
      *           ,             
      *         ,       synchronized      
      *    synchronized      
      */
     BLOCKED,
     /**
      *     ,                             。
      *   ,        Object.wait()            
      * Object.notify()   Object.notifyAll()
      */
     WAITING,
     /**
      *       ,      (WAITING)   ,        ,  
      *               Object.wait(long timeout)   
      * Thread.join(long timeout)        
      */
     TIMED_WAITING,
     /**
      *     ,          
      */
    
    }
    スレッドの動作モードは、まずスレッドを作成し、スレッドの実行に必要な業務方法を指定してからスレッドのstart()を呼び出すと、スレッドはNEW(新規作成)状態からRUNNALE(準備完了)状態になります。
    そしてスレッドは、実行する方法にsynchronized同期コードブロックがあるかどうかを判断し、他のスレッドもこのロックを使っていると、他のスレッドはBLOCKED(ブロッキング待ち)状態になり、他のスレッドがこのロックを使った後、スレッドは残りの方法を実行し続けます。
    Object.wait()やThread.join()の方法があると、スレッドはWAITING(待ち状態)になります。
    タイムアウトの待ち方をしていると、スレッドがTIMED_に入ります。WAITING(タイミング待ち)状態;
    他のスレッドがnotify()またはnotifyAll()を実行した後、スレッドが起動されて残りのトラフィック方法を実行し続け、方法が実行されるまでスレッド全体の流れが終了し、実行フローは下図のようになります。

    【BLOCKEDとWAITINGの違い】
    BLOCKEDとWAITINGは待つという意味がありますが、本質的な違いがあります。
    まず、それらの状態が形成される呼び出し方法が異なります。
    第二に、BLOCKEDは、現在のスレッドがまだアクティブな状態であると理解でき、ブロックして他のスレッドがあるロックリソースを使用し終わるのを待つだけである。
    WAITINGは、Object.wait()またはThread.join()またはLockSupport.park()を呼び出して待機状態に入り、他のスレッドが特定の動作を行うのを待つしかないため、呼び覚まし続けられます。
    例えばスレッドがObject.wait()を呼び出してWAITING状態に入ると、他のスレッドがObject.notify()またはObject.notifyAll()を実行するのを待つ必要があります。
    【start()とrun()の違い】
    まず、Threadソースから見て、start()方法はThread自身の方法であり、synchronizedを使ってスレッドの安全を保証しています。ソースは以下の通りです。
    
    public synchronized void start() {
    
     //     ,    NEW         
    
     if (threadStatus != 0)
    
      throw new IllegalThreadStateException();
    
     //      ,       
    
     group.add(this);
    
     boolean started = false;
    
     try {
    
      start0();
    
      started = true;
    
     } finally {
    
      try {
    
       if (!started) {
    
        group.threadStartFailed(this);
    
       }
    
      } catch (Throwable ignore) {
    
       //        ,   start0     ,            
    
      }
    
     }
    
    }
    run()方法はRunnableの抽象的な方法で、必ず呼び出しの種類からこの方法を書き直さなければなりません。書き直しのrun()方法は実はこのスレッドが実行する業務方法です。ソースは以下の通りです。
    
    public class Thread implements Runnable {
    
     //       ......
    
     private Runnable target;
    
     @Override
    
     public void run() {
    
      if (target != null) {
    
       target.run();
    
      }
    
     }
    
    }
    
    @FunctionalInterface
    
    public interface Runnable {
    
     public abstract void run();
    
    }
    実行の効果から、start()方法はマルチスレッドを開いて、スレッドをNEW状態からRUNNALE状態に変換しますが、run()方法は普通の方法です。
    第二に、それらの使用可能な回数は違っています。start()の方法は何度も呼び出されません。さもなければ、java.lang.Illagal StateExceptionを投げ出します。run()方法は何度も呼び出すことができます。普通の方法ですから。
    【スレッド優先度】
    Threadソースとスレッド優先度に関する属性は3つあります。
    
    //             
    
    public final static int MIN_PRIORITY = 1;
    
    //        
    
    public final static int NORM_PRIORITY = 5;
    
    //             
    
    public final static int MAX_PRIORITY = 10
    スレッドの優先度は、スレッドがCPUタイムプレートを占有する確率と考えられ、優先度が高いスレッドほど優先的に実行される確率が高いが、優先度の高いスレッドが必ず先に実行されるとは保証されない。
    プログラムではThread.set Priorityを通じて優先度を設定できます。set Priorityのソースコードは以下の通りです。
    
    public final void setPriority(int newPriority) {
    
     ThreadGroup g;
    
     checkAccess();
    
     //           
    
     if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
    
      throw new IllegalArgumentException();
    
     }
    
     if((g = getThreadGroup()) != null) {
    
      //                 ,                 
    
      if (newPriority > g.getMaxPriority()) {
    
       newPriority = g.getMaxPriority();
    
      }
    
      setPriority0(priority = newPriority);
    
     }
    
    }
    【スレッドの使い方】
    スレッドの一般的な方法は以下のいくつかあります。
    ジョン()
    スレッドの中でother.joinを呼び出します。この時、現在のスレッドは実行権をotherスレッドに渡します。otherスレッドの実行が終わるか、それともタイムアウト時間が過ぎたら、現在のスレッドを実行し続けます。joinのソースは以下の通りです。
    
    public final synchronized void join(long millis)
    
    throws InterruptedException {
    
     long base = System.currentTimeMillis();
    
     long now = 0;
    
     //          0
    
     if (millis < 0) {
    
      throw new IllegalArgumentException("timeout value is negative");
    
     }
    
     //    0       ,         
    
     if (millis == 0) {
    
      //       (    )      ,     
    
      while (isAlive()) {
    
       wait(0);
    
      }
    
     } else {
    
      //     
    
      while (isAlive()) {
    
       long delay = millis - now;
    
       if (delay <= 0) {
    
        break;
    
       }
    
       wait(delay);
    
       now = System.currentTimeMillis() - base;
    
      }
    
     }
    
    }
    ソースからは、ジョンメソッドの下にあるか、またはwaitメソッドを介して実装されます。
    例えば、join()を使用していない場合、コードは以下の通りです。
    
    public class ThreadExample {
    
     public static void main(String[] args) throws InterruptedException {
    
      Thread thread = new Thread(() -> {
    
       for (int i = 1; i < 6; i++) {
    
        try {
    
         Thread.sleep(1000);
    
        } catch (InterruptedException e) {
    
         e.printStackTrace();
    
        }
    
        System.out.println("     :" + i + " 。");
    
       }
    
      });
    
      thread.start(); //     
    
      //      
    
      for (int i = 1; i < 4; i++) {
    
       try {
    
        Thread.sleep(1000);
    
       } catch (InterruptedException e) {
    
        e.printStackTrace();
    
       }
    
       System.out.println("     :" + i + " 。");
    
      }
    
     }
    
    }
    プログラム実行結果は以下の通りです。
    メインスレッドの睡眠をコピーします。1秒です。
    子スレッド睡眠:1秒。
    メインスレッド睡眠:2秒。
    子スレッド睡眠:2秒。
    メインスレッド睡眠:3秒。
    子スレッド睡眠:3秒。
    子スレッド睡眠:4秒。
    子スレッド睡眠:5秒。
    結果から、ジョイン()を使用していない場合は、メインスレッドが交互に実行されることが分かります。
    その後、私達はまたjoin()の方法をコードに入れます。コードは以下の通りです。
    
    public class ThreadExample {
    
     public static void main(String[] args) throws InterruptedException {
    
      Thread thread = new Thread(() -> {
    
       for (int i = 1; i < 6; i++) {
    
        try {
    
         Thread.sleep(1000);
    
        } catch (InterruptedException e) {
    
         e.printStackTrace();
    
        }
    
        System.out.println("     :" + i + " 。");
    
       }
    
      });
    
      thread.start(); //     
    
      thread.join(2000); //          2   
    
      //      
    
      for (int i = 1; i < 4; i++) {
    
       try {
    
        Thread.sleep(1000);
    
       } catch (InterruptedException e) {
    
        e.printStackTrace();
    
       }
    
       System.out.println("     :" + i + " 。");
    
      }
    
     }
    
    }
    プログラム実行結果は以下の通りです。
    コピー子スレッド睡眠:1秒。
    子スレッド睡眠:2秒。
    メインスレッド睡眠:1秒。
    //thread.join(2000)2秒後にメインスレッドとサブスレッドを交互に実行します。
    子スレッド睡眠:3秒。
    メインスレッド睡眠:2秒。
    子スレッド睡眠:4秒。
    子スレッド睡眠:5秒。
    メインスレッド睡眠:3秒。
    実行結果から、join()メソッドを追加した後、主スレッドは2秒待ちで実行されます。
    yield()
    Threadのソースコードを見れば、yield()は現地の方法であることが分かります。すなわち、yield()はCまたはC+によって実現されたもので、ソースは以下の通りです。
    
    public static native void yield();
    yield()メソッドはスレッドスケジューラに現在のスレッドを出してCPUの使用権を与えるという暗示を示していますが、スレッドスケジューラはこの暗示を無視する可能性があります。
    例えば、私達が実行するこの部分はyield()の方法を含んだコードです。以下の通りです。
    
    public static void main(String[] args) throws InterruptedException {
    
     Runnable runnable = new Runnable() {
    
      @Override
    
      public void run() {
    
       for (int i = 0; i < 10; i++) {
    
        System.out.println("  :" +
    
          Thread.currentThread().getName() + " I:" + i);
    
        if (i == 5) {
    
         Thread.yield();
    
        }
    
       }
    
      }
    
     };
    
     Thread t1 = new Thread(runnable, "T1");
    
     Thread t2 = new Thread(runnable, "T2");
    
     t1.start();
    
     t2.start();
    
    }
    このコードを何回も実行すると、毎回実行する結果が違っています。これはyield()の実行が非常に不安定なため、スレッドスケジューラが必ずしもyield()を使ってCPUの使用権を譲っているとは限りません。
    以上はJAVAスレッド-スレッドの状態を詳しく説明してどれらがありますか?どうやって働いていますか?javaスレッドの詳細については、他の関連記事に注目してください。