Java マルチスレッドの簡単な学習のまとめ

プロセス: 各プロセスには独立したコードとデータ空間 (プロセス コンテキスト) があり、プロセス間の切り替えには大きなオーバーヘッドがかかります。プロセスには 1 ~ n 個のスレッドが含まれます。プロセスはリソース割り当ての最小単位です。

スレッド: 同じタイプのスレッドがコードとデータ空間を共有し、各スレッドが独立した実行スタックとプログラム カウンター (PC) を持ち、スレッド切り替えのオーバーヘッドが小さいです。スレッドは CPU スケジューリングの最小単位です。

スレッドとプロセスは、作成、準備完了、実行、ブロック、終了の 5 つのフェーズに分かれています。

マルチプロセッシングとは、オペレーティング システムが複数のタスク (プログラム) を同時に実行できることを意味します。

マルチスレッドとは、同じプログラム内で複数のシーケンス ストリームを実行することを指します。

Java には一般に 2 つのタイプのマルチスレッドがあり、1 つは Thread クラスを継承するもの、もう 1 つは Runable インターフェースを実装するものです。

1. java.lang.Thread クラスを拡張します。

Thread クラスを継承する方法は、より一般的に使用される方法です。スレッドについて考えるだけで、他に特別な要件がない場合は、Thread を使用できます。

 public class ThreadTest {
    
    
	public static void main(String[] args) {
    
    
		ThreadCustomize mThThreadCustomize1 = new ThreadCustomize("A");
		ThreadCustomize mThThreadCustomize2 = new ThreadCustomize("B");
		
		mThThreadCustomize1.start();
		mThThreadCustomize2.start();
	}
 }
 
 class ThreadCustomize extends Thread{
    
    
	 private String  name;
	 public ThreadCustomize (String name) {
    
    
		 this.name = name;
	 }
	 public void run(){
    
    
		 for(int i = 0; i<5; i++) {
    
    
			 System.out.println( i + ":" + name + "running");
			 try {
    
    
                 Random num = new Random();
				 sleep(num.nextInt(10)*10);
			 }catch (InterruptedException e){
    
    
				 e.printStackTrace();
			 }
		 }
	 }
 }

プログラムが main の実行を開始すると、Java 仮想マシンがプロセスを開始し、main() が呼び出されたときにメイン スレッド main が作成されます。ThreadCustomize の 2 つのオブジェクトの start メソッドを呼び出すと、他の 2 つのスレッドも開始されるため、アプリケーション全体が複数のスレッドで実行されます。

start() メソッドの呼び出し後、マルチスレッドはすぐには実行されませんが、スレッドは実行可能 (Runnable) になり、いつ実行するかはオペレーティング システムによって決定されます。

プログラムの実行結果から、マルチスレッドプログラムが順序どおりに実行されていないことがわかります。したがって、アウトオブオーダー コードのみをマルチスレッド用に設計する必要があります。

Thread.sleep() メソッド呼び出しの目的は、現在のスレッドがプロセスのみで取得した CPU リソースを占有するのを防ぎ、他のスレッドが実行できるように一定の時間を残すことです。
ここに画像の説明を挿入

2. java.lang.Runnable インターフェースを実装する

Runnable を使用することも一般的な方法であり、必要なのは run メソッドを書き換えるだけです。

public class RunnableTest{
    
    
	public static void main(String[] args){
    
    
		new Thread(new RunnableCustomize("C")).start();
		new Thread(new RunnableCustomize("D")).start();
	}
} 
class RunnableCustomize implements Runnable{
    
    
	private String name;
	public RunnableCustomize(String name) {
    
    
		this.name = name;
	}
	
	public void run(){
    
    
		for (int i = 0; i < 5 ; i++) {
    
    
			System.out.println(i + ":" + name + " Running");
			
			try{
    
    
				Random num = new Random();
				sleep(num.nextInt(10)*10);
			}catch(InterruptedException e){
    
    
				e.printStackTrace();
			}
		}
	}
}

操作結果:
ここに画像の説明を挿入

RunnableCustomize クラスは Runnable インターフェイスを実装し、このクラスにマルチスレッドの特性を持たせます。run() メソッドは、マルチスレッド プログラムの規則です。すべてのマルチスレッド コードは run() メソッド内にあります。Thread クラスは、実際には Runnable インターフェイスを実装するクラスです。

マルチスレッドを開始するときは、Thread クラスのコンストラクター Thread(Runnable target) を通じてオブジェクトを構築し、Thread オブジェクトの start() メソッドを呼び出してマルチスレッド コードを実行する必要があります。

実際、すべてのマルチスレッド コードは、Thread の start () メソッドを実行することによって実行されます。したがって、Thread クラスを拡張する場合でも、Runnable インターフェイスを実装してマルチスレッドを実現する場合でも、スレッドは最終的に Thread オブジェクトの API を通じて制御されます。

3. スレッドとランナブルの違い

Thread を継承したクラスはリソース共有には適していません。しかし、Runable インターフェースが実装されていれば、リソース共有を簡単に実現できます。

Thread クラスを継承するよりも、Runable インターフェイスを実装することの利点は次のとおりです。

  1. 同じプログラム コードの複数のスレッドが同じリソースを処理するのに適しています
  2. Javaの単一継承の制限を回避できる
  3. プログラムの堅牢性が向上し、コードを複数のスレッドで共有でき、コードとデータが独立します。
  4. スレッド プールは、Runable クラスまたは呼び出し可能クラスを実装するスレッドにのみ配置でき、Thread を継承するクラスに直接配置することはできません。

4. スレッドの状態遷移

ここに画像の説明を挿入

  1. 新しい状態(New): スレッドオブジェクトが新規に作成されます。
  2. 準備完了状態 (実行可能): スレッド オブジェクトが作成された後、他のスレッドがオブジェクトの start() メソッドを呼び出します。この状態のスレッドは実行可能なスレッド プールに配置され、実行可能になり、CPU の使用権を取得するのを待ちます。
  3. 実行状態 (Running): 準備完了状態のスレッドが CPU を獲得し、プログラム コードを実行します。
  4. ブロック状態 (Blocked): ブロック状態とは、スレッドが何らかの理由で CPU の使用権を放棄し、一時的に実行を停止する状態です。スレッドが準備完了状態になるまで、実行状態に移行する機会があります。ブロックには 3 つのタイプがあります。
    • ブロックを待機中: 実行中のスレッドは wait() メソッドを実行し、JVM はスレッドを待機プールに入れます。(待機すると保持されていたロックが解除されます)
    • 同期ブロック: 実行中のスレッドがオブジェクトの同期ロックを取得するときに、同期ロックが他のスレッドによって占有されている場合、JVM はスレッドをロック プールに入れます。
    • その他のブロック: 実行中のスレッドが sleep() または join() メソッドを実行するとき、または I/O リクエストが発行されるとき、JVM はスレッドをブロック状態にします。sleep () 状態がタイムアウトになると、join () はスレッドが終了するかタイムアウトになるまで待機します。あるいは、I/O 処理が完了すると、スレッドは再び準備完了状態に移行します。(スリープしても保持されたロックは解除されません)
  5. デッド状態 (Dead): スレッドは実行を終了するか、例外により run() メソッドを終了し、スレッドはライフサイクルを終了します。

5. スレッドのスケジュール設定

  1. スレッドの優先順位を調整する: Java スレッドには優先順位があり、優先順位の高いスレッドほど実行される機会が多くなります。

    Java スレッドの優先度は整数で表され、値の範囲は 1 ~ 10 です。Thread クラスには次の 3 つの静的定数があります。

    static int MAX_PRIORITY      //线程可以具有的最高优先级,取值为10
    static int MIN_PRIORITY      //线程可以具有的最低优先级,取值为1
    static int NORM_PRIORITY     //分配给线程的默认优先级,取值为5
    

    Thread クラスの setPriority() メソッドと getPriority() メソッドは、それぞれスレッドの優先順位を設定および取得するために使用されます。

    各スレッドにはデフォルトの優先順位があります。メインスレッドのデフォルトの優先順位は Thread.NORM_PRIORITY です。

    スレッドの優先順位には継承関係があり、たとえば、スレッド A 内にスレッド B が作成された場合、B は A と同じ優先順位になります。

    JVM には 10 個のスレッド優先順位が用意されていますが、どれも一般的なオペレーティング システムとうまく対応しません。プログラムをさまざまなオペレーティング システムに移植する場合は、同じ優先順位で同じスケジューリング方法が使用されるように、Thread クラスの次の 3 つの静的定数のみを優先順位として使用する必要があります。

  2. スレッド スリープ: Thread.sleep(long millis) メソッドは、スレッドをブロック状態にします。millis パラメータはスリープ時間をミリ秒単位で設定します。スリープが終了すると、Ready (Runnable) 状態になります。sleep() はプラットフォームの移植性に優れています。

  3. 待機中のスレッド: Object クラスの wait() メソッドにより、現在のスレッドは、他のスレッドがこのオブジェクトの Notice() メソッドまたは NotifyAll() ウェイクアップ メソッドを呼び出すまで待機します。これら 2 つのウェイクアップ メソッドも Object クラスのメソッドであり、その動作は wait(0) を呼び出すことと同等です。

  4. スレッドの収量: Thread.yield() メソッドは、現在実行中のスレッド オブジェクトを一時停止し、同じかそれより高い優先度を持つスレッドに実行の機会を与えます。

  5. スレッド結合: join() メソッド。他のスレッドが終了するまで待ちます。現在のスレッドで別のスレッドの join() メソッドが呼び出された場合、現在のスレッドは他のプロセスの実行が終了するまでブロック状態になり、その後、現在のスレッドはブロック状態から準備完了状態に変わります。

  6. スレッドのウェイクアップ: Object クラスの Notice() メソッドは、このオブジェクトのモニターを待機している単一のスレッドをウェイクアップします。すべてのスレッドがこのオブジェクトを待機している場合、スレッドの 1 つが選択されて起動されます。選択は任意であり、実装に関する決定が行われるときに行われます。スレッドは、その wait メソッドの 1 つを呼び出して、オブジェクトのモニターを待機します。現在のスレッドがこのオブジェクトのロックを放棄するまで、目覚めたスレッドは実行を続けることができます。目覚めたスレッドは、このオブジェクト上でアクティブに同期している他のすべてのスレッドと通常の方法で競合します。たとえば、目覚めたスレッドには、このオブジェクトをロックする次のスレッドとなる信頼性の高い特権や不利な点がありません。同様のメソッドには、このオブジェクト モニターで待機しているすべてのスレッドを起動するnotifyAll()もあります。

6. 共通機能の説明

  1. sleep(long millis): 指定されたミリ秒数以内に現在実行中のスレッドをスリープします (実行を一時停止します)。

  2. join(): スレッドが終了するまで待ちます

    使用方法: join() は Thread クラスのメソッドです。スレッドの開始直後に呼び出され、join() の機能は「スレッドの終了を待つ」です。ここで理解する必要があるのは、スレッドとは、子スレッドが終了するのを待っているメインスレッドを指します。つまり、サブスレッドが join() メソッドを呼び出した後のコードは、サブスレッドが終了するのを待った後にのみ実行できます。

    Thread t = new Thread();
    t.start();
    t.join();
    

    join() メソッドを使用する利点: 多くの場合、メインスレッドはサブスレッドを生成して開始しますが、サブスレッドが多くの時間のかかる計算を実行する必要がある場合、多くの場合、メインスレッドはサブスレッドより前に終了します。ただし、メインスレッドが他のトランザクションの処理を終了した場合、最後に、サブスレッドの処理結果を使用する必要があります。つまり、メインスレッドは、サブスレッドが完了するまで待ってから終了する必要があります。 join()メソッドを使用します。

    join() メソッドを追加しない場合:

     public class ThreadTest {
          
          
    	public static void main(String[] args) {
          
          
    		System.out.println (Thread.currentThread().getName() + " 线程运行开始!");
    		ThreadCustomize mThThreadCustomize1 = new ThreadCustomize("A");
    		ThreadCustomize mThThreadCustomize2 = new ThreadCustomize("B");
    
    		mThThreadCustomize1.start();
    		mThThreadCustomize2.start();
    		System.out.println (Thread.currentThread().getName() + " 线程运行结束!");
    	}
     }
    
     class ThreadCustomize extends Thread{
          
          
    	 private String  name;
    	 public ThreadCustomize (String name) {
          
          
    		 super(name);
    		 this.name = name;
    	 }
    	 public void run(){
          
          
    		 System.out.println (Thread.currentThread().getName() + " 线程运行开始!");
    		 for(int i = 0; i<5; i++) {
          
          
    			 System.out.println(name + " running  "  + i);
    			 try {
          
          
                    Random num = new Random();
    				sleep(num.netInt(10)*10);
    			 }catch (InterruptedException e){
          
          
    				 e.printStackTrace();
    			 }
    		 }
    		 System.out.println (Thread.currentThread().getName() + " 线程运行结束!");
    	 }
     }
    

    操作結果:
    ここに画像の説明を挿入

    子スレッドが終了する前にメインスレッドが終了する可能性があることが判明しました。

    join() メソッドを追加します。

    import java.lang.Thread ;
    import java.util.Random;
    
     public class ThreadTest {
          
          
    	public static void main(String[] args) {
          
          
    		System.out.println (Thread.currentThread().getName() + " 线程运行开始!");
    		ThreadCustomize mThThreadCustomize1 = new ThreadCustomize("A");
    		ThreadCustomize mThThreadCustomize2 = new ThreadCustomize("B");
    
    		mThThreadCustomize1.start();
    		mThThreadCustomize2.start();
    		try{
          
          
    			mThThreadCustomize1.join();
    		}catch (InterruptedException e){
          
          
    			e.printStackTrace();
    		}
    		try{
          
          
    			mThThreadCustomize2.join();
    		}catch (InterruptedException e){
          
          
    			e.printStackTrace();
    		}
    		System.out.println (Thread.currentThread().getName() + " 线程运行结束!");
    	}
     }
    
     class ThreadCustomize extends Thread{
          
          
    	 private String  name;
    	 public ThreadCustomize (String name) {
          
          
    		 super(name);
    		 this.name = name;
    	 }
    	 public void run(){
          
          
    		 System.out.println (Thread.currentThread().getName() + " 线程运行开始!");
    		 for(int i = 0; i<5; i++) {
          
          
    			 System.out.println(name + " running  "  + i);
    			 try {
          
          
    				 Random num = new Random();
    				 sleep(num.nextInt(1000));
    			 }catch (InterruptedException e){
          
          
    				 e.printStackTrace();
    			 }
    		 }
    		 System.out.println (Thread.currentThread().getName() + " 线程运行结束!");
    	 }
     }
    

    操作結果:
    ここに画像の説明を挿入

    メインスレッドは子スレッドの後に終了します。

  3. yield(): 現在実行中のスレッド オブジェクトを一時停止し、他のスレッドを実行します。

    yield() が行うべきことは、現在実行中のスレッドを実行可能な状態に戻し、同じ優先順位の他のスレッドが実行できるようにすることです。したがって、yield() を使用する目的は、同じ優先順位のスレッド間で適切なラウンドロビン実行を可能にすることです。ただし、実際には、yield() が生成に失敗するという保証はありません。生成されたスレッドはスレッド スケジューラによって再度選択される可能性があるためです。

    yield() によってスレッドが待機、スリープ、ブロック状態になることはありませんでした。ほとんどのツリーの場合、yeild() はスレッドを実行状態から実行可能状態にしますが、効果がない場合もあります。

    public class YieldTest{
          
          
    	public static void main(String[] args){
          
          
    		Thread mThreadCustomize1 = new ThreadCustomize ("A");
    		Thread mThreadCustomize2 = new ThreadCustomize ("B");
    		
    		mThreadCustomize1.start();
    		mThreadCustomize2.start();
    	}
    }
    class ThreadCustomize extends Thread{
          
          
    	private String  name;
    	public ThreadCustomize(String name){
          
          
    		super(name);
    	}
    	@Override
    	public void run(){
          
          
    		for(int i = 0; i <50; i++){
          
          
    			System.out.println("" + this.getName() + "=====" + i);
    			if (i == 30){
          
          
    				this .yield();
    			}
    		}
    	}
    }
    

    操作結果:

    • 最初のケース: A スレッドは 30 まで実行すると CPU 時間を失い、その後 B スレッドが CPU 時間を取得して実行します。
    • 2 番目の状況: スレッド A が 30 まで実行すると、CPU 時間が失われますが、この時点でスレッド A が CPU 時間を取得して実行します。

    sleep() と yield() の違い:

    sleep() は現在のスレッドを停止状態にするため、sleep() を実行しているスレッドは指定された時間内に実行されません。yield() は現在のスレッドを実行可能状態に戻すだけなので、yield() を実行しているスレッドは) 実行可能状態になったらすぐに実行可能です。

    sleep() メソッドは、現在実行中のスレッドを一定期間スリープさせ、非実行状態に入ります。この期間の長さはプログラムによって設定されます。 yield() メソッドは、現在のスレッドに CPU の所有権を放棄させます。ただし、放棄時間を設定することはできません。実際、yield() メソッドはそのような操作に対応します。まず、実行可能な状態にある同じ優先度のスレッドが現在存在するかどうかを確認し、存在する場合は、CPU にこのスレッドに対する所有権を与えます。そうでない場合は、元のスレッドの実行を継続します。糸。したがって、yield() メソッドは「ギブイン」と呼ばれ、同じ優先順位の他のスレッドに実行の機会を与えます。

    さらに、 sleep() メソッドは、優先度の低いスレッドにも実行の機会を与えますが、 yield() メソッドが実行されるとき、現在のスレッドはまだ実行可能な状態にあるため、優先度の低いスレッドは実行できません。 CPU の所有権を取得するスレッド。実行中のシステムで、優先度の高いスレッドが sleep() メソッドを呼び出さず、I/O ブロックを受けていない場合、優先度の低いスレッドは、優先度の高いスレッドがすべて実行を終了するまで待つことしかできません。走る。

  4. setPriority(): スレッドの優先順位を変更します。

    MIN_PRIORITY = 1

    NORW_PRIORITY = 5

    MAX_PRIORITY = 10

    使用法:

    ThreadCustomiza t1 = new ThreadCustomiza("t1");
    ThreadCustomiza t2 = new ThreadCustomiza("t2");
    t1.setPriority(Thread.MAX_PRIORITY);
    t2.setPriority(Thread.MIN_PRIORITY);
    
  5. Interrup(): 特定のスレッドを中断する代わりに、スレッドに割り込み信号を送信するだけです。その結果、スレッドは無期限に待機している間 (デッドロックなど)、割り込み信号をスローしてスレッドを終了できます。ただし、この例外を食べると、 , そうすればスレッドは中断されなくなります。

  6. 待って()

    Obj.wait() と Obj.notify() は synchronized (Obj) と一緒に使用する必要があります。つまり、取得された Obj ロックに対して wait と notification が動作します。文法の観点から見ると、それは Obj.wait( )、Obj.notify synchronize(Obj){…} ステートメント ブロック内にある必要があります。機能的には、待機とは、スレッドがオブジェクト ロックを取得した後、積極的にオブジェクト ロックを解放し、同時にスレッドがスリープすることを意味します。他のスレッドがオブジェクトのnotify()を呼び出してスレッドを起動するまで、オブジェクトのロックを取得して実行を続行できます。対応するnotify()は、オブジェクトロックのウェイクアップ操作です。ただし、注意すべき点は、notify() が呼び出された後、オブジェクトのロックはすぐには解放されませんが、対応する synchronize(){} ステートメント ブロックの実行が完了した後、ロックが自動的に解放された後、JVM はロックされるということです。 wait() のオブジェクト スレッドの中からランダムにスレッドを選択し、それにオブジェクト ロックを与え、スレッドをウェイクアップして、実行を継続します。これにより、スレッド間の同期とウェイクアップ操作が提供されます。Thread.sleep() と Object.wait() はどちらも現在のスレッドを一時停止して CPU の制御を解放できますが、主な違いは、Object.wait() は CPU を解放すると同時にオブジェクト ロックの制御も解放することです。

    例: 3 つのスレッドを作成します。スレッド A は A を 10 回出力し、スレッド B は B を 10 回出力し、スレッド C は C を 10 回出力します。スレッドは同時に実行する必要があり、ABC を交互に 10 回出力します。

    public class MyThreadPrinter2 implements Runnable {
          
             
    	  
        private String name;   
        private Object prev;   
        private Object self;   
      
        private MyThreadPrinter2(String name, Object prev, Object self) {
          
             
            this.name = name;   
            this.prev = prev;   
            this.self = self;   
        }   
        @Override  
        public void run() {
          
            
            int n = 10;
            for (int i = 0; i < n; i++) {
          
          
                synchronized (prev) {
          
          
                    synchronized (self) {
          
          
                        System.out.print(name);
                        //唤醒本对象的监视器上的等待线程
                        self.notify();
                    }
                    try {
          
          
                        if (i < n-1)
                            //如果不是最后一次循环,则将该线程挂到prev对象的监视器上,进行等待...
                            prev.wait();
    						//否则,最后一次循环不能再wait()了,不然就会死锁。
                    }catch (InterruptedException e) {
          
          
                        e.printStackTrace();
                    }
                }
            }
        }   
        public static void main(String[] args) throws Exception {
          
             
            Object a = new Object();   
            Object b = new Object();   
            Object c = new Object();   
            MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);   
            MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);   
            MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);   
               
            new Thread(pa).start();
            Thread.sleep(100);  //确保按顺序A、B、C执行
            new Thread(pb).start();
            Thread.sleep(100);  
            new Thread(pc).start();   
            Thread.sleep(100);  
            }   
    }  
    

    アイデア: 一般的な観点から見ると、問題は 3 つのスレッド間の同期ウェイクアップ操作であり、主な目的は、ThreadA->ThreadB->ThreadC->ThreadA がループ内で 3 つのスレッドを実行することです。スレッドの実行順序を制御するには、起動と待機の順序を決定する必要があるため、各スレッドは実行を継続するために同時に 2 つのオブジェクト ロックを保持する必要があります。オブジェクト ロックは prev で、前のスレッドによって保持されているオブジェクト ロックです。もう 1 つは自己オブジェクト ロックです。主な考え方は、実行順序を制御するには、最初に prev ロックを保持する必要がある、つまり、前のスレッドが独自のオブジェクト ロックを解放し、次に独自のオブジェクト ロックを適用し、両方が利用可能になったときに出力する必要があるということです。次に、最初に self.notify() を呼び出し、自身のオブジェクト ロックを解放し、次の待機スレッドを起動します。次に、 prev.wait() を呼び出して前のオブジェクト ロックを解放し、現在のスレッドを終了し、待機ループの後に再び起動します。終わります。上記のコードを実行すると、3 つのスレッドが ABC を周期的に合計 10 回出力していることがわかります。プログラム実行の主なプロセスは、A スレッドが最初に実行され、C と A のオブジェクト ロックを保持し、次に A と C のロックを解放し、B をウェイクアップすることです。スレッド B は A ロックを待機し、次に B ロックを適用し、次に B を出力し、その後 B、A ロックを解放し、C をウェイクアップします。スレッド C は B ロックを待機し、次に C ロックを適用し、次に C を出力し、その後 C、B を解放します鍵をかけ、Aを起こします。一見問題が無いように見えますが、よく考えてみると問題があることが分かります これが初期状態で、3つのスレッドがA、B、Cの順に起動されます。以前の考えでは、A は B を目覚めさせ、B は C を目覚めさせ、C は再び A を目覚めさせます。ただし、この仮定は、JVM でのスレッドのスケジューリングと実行の順序に依存します。

    待機とスリープの違い:

    共通点:

    1. これらはすべてマルチスレッド環境にあり、プログラムが呼び出されたときに、指定されたミリ秒数ブロックして復帰することができます。

    2. wait() と sleep() はどちらも、interrupt() メソッドを通じてスレッドの一時停止状態に割り込むことができるため、スレッドはすぐに InterruptedException をスローします。

      スレッド A がスレッド B をすぐに終了したい場合は、スレッド B に対応する Thread インスタンスの割り込みメソッドを呼び出すことができます。このスレッド B が待機中、スリープ中、または参加中の場合、スレッド B はすぐに InterruptedException をスローし、catch(){} に直接戻ってスレッドを安全に終了します。

      InterruptedException は、interrupt() メソッドによってではなく、スレッド自体からスローされることに注意してください。スレッド上で中断()が呼び出されたとき、スレッドが通常のコードを実行している場合、スレッドは InterruptedException をまったくスローしません。ただし、スレッドが wait()/sleep()/join() に入ると、すぐに InterruptedException がスローされます。

    違い:

    1. Thread クラスのメソッド: sleep().yield など。

      オブジェクトメソッド:wait()、notify()など。

    2. 各オブジェクトには、同期アクセスを制御するためのロックがあります。Synchronize キーワードは、オブジェクトのロックと対話して、スレッドの同期を実現できます。

      sleep メソッドはロックを解放しませんが、wait メソッドはロックを解放して、他のスレッドが同期制御ブロックまたはメソッドを使用できるようにします。

    3. wait()、notify()、notifyAll() は同期制御メソッドまたは同期制御ブロックでのみ使用できますが、sleep はどこでも使用できます

      したがって、sleep() メソッドと wait() メソッドの最大の違いは次のとおりです。

      sleep() がスリープしているとき、オブジェクトのロックを維持し、引き続きロックを占有します

      wait() がスリープしたら、オブジェクトのロックを解除します

      ただし、wait() と sleep() は両方とも、interrupt() メソッドを通じてスレッドの一時停止状態に割り込むことができるため、スレッドはすぐに InterruptedException をスローします (ただし、このメソッドは推奨されません)。

    sleep()メソッド:現在のスレッドを停滞状態にし、CPUの使用を放棄する目的は、現在のスレッドがプロセスのみで獲得したCPUリソースを占有することを防ぎ、一定の時間を空けることです。実行する他のスレッド。sleep() は Thread クラスの静的メソッドであるため、オブジェクトのマシン ロックを変更できません。そのため、sleep() メソッドが Synchronized ブロック内で呼び出された場合、スレッドはスリープしますが、オブジェクトのマシン ロックは変更されません。解放され、他のスレッドはこのオブジェクトにアクセスできません(停止してもオブジェクトのロックは保持されます)。sleep() のスリープ時間が経過すると、このスレッドの優先順位が高くない限り、他のスレッドが実行中であり、実行を放棄するようにスケジュールされていない可能性があるため、スレッドはすぐに実行されない可能性があります。

    wait() メソッド: Object クラスに属するメソッド; スレッドが wait() メソッドを実行すると、オブジェクトに関連する待機プールに入り、同時にオブジェクトのマシン ロックを解放します (マシンを一時的に失います)。 lock、wait(長いタイムアウト) タイムアウト後にオブジェクト ロックを返す必要がある); 他のスレッドがアクセスできる; wait() は、notify または NoticeAll を使用するか、現在の待機プール内のスレッドをウェイクアップするためのスリープ時間を指定します。wait() は Synchronized ブロックに配置する必要があります。そうしないと、プログラムの実行中に「java.lang.IllegalMonitorStateException」例外がスローされます。

7. 共通スレッド名の説明

メインスレッド: JVM 呼び出しプログラム main() によって生成されるスレッド

現在のスレッド: 一般に、スルー スレッドを指します。currentThread() を使用してプロセスを取得します。

バックグラウンド スレッド: 他のスレッドにサービスを提供するスレッドを指し、デーモン スレッドとも呼ばれます。JVM のガベージ コレクション スレッドはバックグラウンド スレッドです。ユーザースレッドとデーモンスレッドの違いは、メインスレッドの終了に応じてメインスレッドの終了を待つかどうかです。

フォアグラウンド スレッド: バックグラウンド スレッドのサービスを受け付けるスレッドを指します。実際には、操り人形と背後のマニピュレーターの関係のように、フォアグラウンドの背後にあるスレッドは相互にリンクされています。パペットはフォアグラウンド スレッドであり、舞台裏のマニピュレータはバックグラウンド スレッドです。フォアグラウンド スレッドによって作成されたスレッドも、デフォルトではフォアグラウンド スレッドになります。isDaemon() メソッドと setDaemon() メソッドを使用して、スレッドがバックグラウンド スレッドかどうかを判断および設定できます。

スレッドクラスの一般的なメソッド:

方法 効果
寝る() スレッドを強制的に n ミリ秒間スリープ状態にします
生きている() スレッドが生きているかどうかを判断する
加入() スレッドが終了するまで待ちます
アクティブカウント() プログラム内のアクティブなスレッドの数
列挙() プログラム内のスレッドを列挙します
currentThread() 現在のスレッドを取得する
isDaemon() スレッドがデーモン スレッドであるかどうか
setDaemon() スレッドをデーモンスレッドとして設定する
setName() スレッドの名前を設定する
待って() スレッドを強制的に待機させる
通知() スレッドに実行を継続するように通知する
setPriority() スレッドの優先順位を設定する

8. スレッドの同期

  1. Synchronized キーワードには 2 つの機能があります。
    • オブジェクト インスタンスでは、Synchronized aMethod(){} により、複数のスレッドがこのオブジェクトの同期メソッドに同時にアクセスするのを防ぐことができます (オブジェクトに複数の同期メソッドがある場合、1 つのスレッドが同期メソッドの 1 つにアクセスしている限り、他のスレッドはこのオブジェクト内の同期されたメソッドに同時にアクセスすることはできません)。現時点では、異なるオブジェクト インスタンスの同期メソッドは相互に干渉しません。つまり、他のスレッドは、同じクラスの別のオブジェクト インスタンスの同期されたメソッドに同時にアクセスできます。
    • これは特定の型の範囲であり、同期静的 aStaticMethod{} により、複数のスレッドがこのクラスの同期静的メソッドに同時にアクセスすることができなくなります。すべてのオブジェクト インスタンスで機能します。
  2. メソッドの前で synchronized キーワードを使用するだけでなく、メソッド内の特定のブロックでも synchronized キーワードを使用でき、このブロックのリソースのみが相互に排他的であることを示します。使用法: synchronized(this){/*block*/}、そのスコープは現在のオブジェクトです。
  3. synchronized キーワードは継承できません。つまり、基本クラスのメソッド synchronized f() {} は、継承されたクラスでは自動的に synchronized f() {} にならず、 f() {} になります。継承では、そのメソッドの 1 つを同期メソッドとして明示的に指定する必要があります。

要約:

結論は:

  1. スレッド同期の目的は、複数のスレッドがリソースにアクセスするときにリソースが損傷するのを防ぐことです。
  2. スレッド同期メソッドはロックを通じて実装されます。各オブジェクトにはロックが 1 つだけあります。このロックは特定のオブジェクトに関連付けられます。スレッドがオブジェクト ロックを取得すると、そのオブジェクトにアクセスする他のスレッドは、そのオブジェクトの他のスレッドにアクセスできなくなります。非同期方法
  3. 静的同期メソッドの場合、ロックはこのクラス用であり、ロック オブジェクトはこのクラスの Class オブジェクトです。静的メソッドと非静的メソッドのロックは相互に干渉しません。スレッドがロックを取得し、同期メソッド内の別のオブジェクトの同期メソッドにアクセスすると、両方のオブジェクトのロックが取得されます。
  4. 同期の場合、どのオブジェクトと同期するかを常に意識することが重要です。
  5. スレッドセーフなクラスを作成するには、リソースへのアクセスを競合する複数のスレッドのロジックとセキュリティを正しく判断し、「アトミック」操作を分析し、アトミック操作中に他のスレッドが競合するリソースにアクセスできないようにすることに常に注意を払う必要があります。
  6. 複数のスレッドがオブジェクトのロックを待機している場合、ロックを取得していないスレッドはブロックされます。
  7. デッドロックは、スレッドが互いにロックを待機することによって発生しますが、実際に発生する可能性は非常に低いです。

おすすめ

転載: blog.csdn.net/qq_43880417/article/details/120569678