ダークホースプログラマー_Javaマルチスレッドベース


--------Androidトレーニング、、Javaトレーニング、、Java学習型技術ブログあなたとの交流を楽しみにしています! ------------
マルチスレッドを学ぶには、マルチスレッドの概念を徹底的に理解しなければならない.
プログラム:
プログラムは静的なコードであり、アプリケーションが実行するブルーブックである.
プロセス:
プロセスはプログラムの動的実行プロセスであり、コードのロード、実行から実行までの完全なプロセスに対応し、このプロセスもプロセス自体が発生し、消滅するまでのプロセスである.
スレッド:
スレッドはプロセスより小さい単位であり、1つのプロセスの実行過程で複数のスレッドを生成することができ、各スレッドには自身の発生、存在、消滅の過程があり、動的な概念でもある.
各プロセスには専用のメモリ領域があり、スレッド間で同じメモリ領域(コードとデータを含む)を共有することができ、これらの共有ユニットを利用してデータ交換、リアルタイム通信と必要な同期操作を実現することができる.
メインスレッド:
Javaプログラムごとにデフォルトのプライマリ・スレッドがあります.Javaプログラムは、常にプライマリクラスのmainメソッドから実行されます.JVMがコードをロードすると、mainメソッドが発見するとスレッドが起動し、このスレッドは「メインスレッド」と呼ばれ、mainメソッドの実行を担当する.
mainメソッドで再作成されるスレッドは、他のスレッドです.
 
Javaでは、スレッドを作成する方法は2つあります.
1、Threadクラスを継承し、run()メソッドを上書きする:Threadサブクラスを使用してスレッドを作成する利点は、サブクラスに新しいメンバー変数またはメソッドを追加し、スレッドに何らかの属性または機能を持たせることである.しかしJavaはマルチ継承をサポートせず、Threadクラスのサブクラスは他のクラスを拡張できない.
2、Runnableインタフェースの実装:Threadクラスでスレッドオブジェクトを直接作成し、コンストラクション関数Thread(Runnable target)(パラメータtargetはRunnableインタフェース)を使用して、スレッドオブジェクトを作成する際に、作成したスレッドのターゲットオブジェクトと呼ばれるRunnableインタフェースクラスを実装するインスタンスを構築方法パラメータに渡す必要があります.スレッドがstart()メソッドを呼び出すと、CPUリソースが使用される順番になると、ターゲットオブジェクトはインタフェースのrun()メソッド(インタフェースコールバック)を自動的に呼び出す.
スレッド間で同じメモリユニット(コードとデータを含む)を共有し、これらの共有ユニットを利用して、データ交換、リアルタイム通信、および必要な同期動作を実現することができる.Thread(Runnable target)が作成する同じターゲットオブジェクトを使用するスレッドについては、そのターゲットオブジェクトのメンバー変数とメソッドを共有することができる.
また、ターゲットオブジェクトクラスを作成するには、必要に応じて特定のクラスのサブクラスであってもよいため、Runnableインタフェースを使用することは、Threadを使用するサブクラスよりも柔軟性がある.
スレッドの一般的な方法:
1.start():スレッド呼び出しこのメソッドはスレッドを起動し、新規状態から準備キューに入り、CPUリソースを楽しむと作成したスレッドから離れ、独立して独自のライフサイクルを開始することができる.
2.run():Threadクラスのrun()メソッドは、Runnableインタフェースのrun()メソッドと同じ機能と役割を果たし、スレッドオブジェクトがスケジューリングされた後に行われる操作を定義するために使用され、システムが自動的に呼び出され、ユーザーが参照できないメソッドです.run()メソッドの実行が完了すると、スレッドは死亡状態になります.すなわち、スレッドはそれに割り当てられたメモリを解放します(死亡状態スレッドはstart()メソッドを呼び出すことはできません).スレッドがrun()メソッドを終了するまで、スレッドにstart()メソッドを呼び出すことはできません.そうしないと、IllegalThreadStateException異常が発生します.
sleep(int millsecond):優先度の高いスレッドは、優先度の低いスレッドに協力する必要がある場合があります.この場合、優先度の高いスレッドにCPUリソースを譲り、優先度の低いスレッドを実行する機会を与えるために、sleep(int millsecond)メソッドを使用します.スレッドがスリープ中に中断されると、JVMはInterruptedException異常を放出します.したがって、try-catch文ブロックでsleepメソッドを呼び出す必要がある.
3.isAlive():スレッドがstart()メソッドを呼び出してCPUリソースを占有すると、そのスレッドのrun()メソッドが実行を開始し、run()メソッドが終了する前にisAlive()を呼び出してtrueを返し、スレッドが新規状態または死亡状態にある場合にisAlive()を呼び出してfalseを返す.
4.currentThread():Threadクラスのクラスメソッドであり、クラス名で呼び出し、現在CPUリソースを使用しているスレッドを返すことができます.
5.interrupt():スレッド呼び出しsleep()メソッドがスリープ状態にある場合、CPUリソースを占有するスレッドは、スリープしたスレッド呼び出しinterrupt()メソッドを「起こさせる」ことができ、すなわちスレッドにIllegalThreadStateException異常が発生し、スリープを終了し、CPUリソースを再キューして待つことができる.
 
スレッドの状態:
スレッドは一定の条件下で状態が変化する.スレッドの変化のステータス変換図は次のとおりです.
1、新規ステータス(New):スレッドオブジェクトが新しく作成されました.
2、準備状態(Runnable):スレッドオブジェクトが作成されると、他のスレッドがそのオブジェクトのstart()メソッドを呼び出します.この状態のスレッドは実行可能スレッドプールにあり,実行可能となり,CPUの使用権の取得を待つ.
3、運転状態(Running):準備状態のスレッドはCPUを取得し、プログラムコードを実行する.
4、ブロック状態(Blocked):ブロック状態はスレッドが何らかの理由でCPU使用権を放棄し、一時的に運転を停止することである.スレッドが準備完了状態になるまで、実行状態に移行する機会はありません.ブロックの場合は3つに分けられます.
(一)、待機ブロック:実行中のスレッドはwait()メソッドを実行し、JVMはそのスレッドを待機プールに入れる.
(二)、同期ブロック:実行するスレッドはオブジェクトの同期ロックを取得する時、その同期ロックが他のスレッドに占有されると、JVMはそのスレッドをロックプールに入れる.
(3)、その他のブロック:実行中のスレッドがsleep()またはjoin()メソッドを実行したり、I/O要求が発行されたりすると、JVMはそのスレッドをブロック状態にします.sleep()ステータスがタイムアウトし、join()がスレッドの終了またはタイムアウトを待つか、I/O処理が完了すると、スレッドは再び準備完了ステータスに移行します.
5、死亡状態(Dead):スレッドが実行済みまたは異常によりrun()メソッドを終了し、このスレッドはライフサイクルを終了する.
 
スレッドの作成方法:
1.Threadクラスを継承しrun()メソッドを上書きする
概略構成は次のとおりです.
 
class    extends Thread{ 

   ; 
 … 
 public void run(){ 
.....
 } //    run()  

  ;
 … 
   
 } 
 

 
簡単な例:
 
 
 public class ThreadDemo
 {
 	public static void main(String[] args)
 	{
 		Demo demo = new Demo();
 		
 		demo.start();
 		
 		for(int i = 1; i <= 100; i++)
 		{
 			System.out.println("main Run( )" + i);
 		}
 	}
 }
 
 class Demo extends Thread
 {
 	public void run()
 	{
 		for(int i = 1; i <= 100; i++)
 		{
 			System.out.println("Demo Run( )" + i);
 		}
 	}
 }
 
 
実行結果はmainとDemoメソッドが交互に表示されます..
ループ回数が多いため、クリップの一部のみを切り取ります.
 
...................
Demo Run( )93
 Demo Run( )94
 Demo Run( )95
 Demo Run( )96
 Demo Run( )97
 Demo Run( )98
 Demo Run( )99
 Demo Run( )100
 main Run( )48
 main Run( )49
 main Run( )50
 main Run( )51
 main Run( )52
 main Run( )53
 main Run( )54
...................
 
実行するたびに結果が異なります.複数のスレッドがcpuの実行権を取得するため、cpuが誰に実行するか、誰が実行します.マルチスレッドの特性:ランダム性.
 
上記のコードのdemoをstart()をdemoに変更します.run().このときは単一スレッドに類似する、まずDemo Run(副)を実行してからmain Run(主)を実行する.
結果分析:
run()メソッドを直接呼び出す場合、新しいスレッドを使用して実行する、メインスレッドの内部で実行するのではなく、start()を呼び出すと、新しいプロセスが開く、新しいプロセスでこのセグメントメソッドが実行されることを示す.
直接呼び出しrun()メソッドは、主メソッドにおけるオブジェクトが本クラスのメソッドを直接呼び出すことに相当する.
2.Runnableインタフェースの実装
具体的な手順:
 
--クラス実装Runnableインタフェースの定義
--Runnableインタフェースのrunメソッドを上書きし、スレッドが実行されるコードをrunメソッドに配置します.
--Threadクラスによるスレッドオブジェクトの作成
--Runnableインタフェースのサブクラスオブジェクトを実際のオブジェクトとしてThreadクラスのコンストラクション関数に渡します.カスタムrunメソッドが属するオブジェクトはRunnableインタフェースのサブクラスオブジェクトなので、スレッドにオブジェクトを明確に指定させるrunメソッドです
--Threadクラスのstartメソッドを呼び出してスレッドを開く.
概略構成は次のとおりです.
 
class    implements Runnable{ 
   ;
 … 
 public void run(){ 
.....
 } //     run()  
   ; 
   ....
 } 
 

簡単な例:
 
 class TicketRun implements Runnable
 {
 	private int ticket = 100;
 
 	public void run()
 	{
 		while (true)
 		{
 			if (ticket > 0)
 			{
 				System.out.println(Thread.currentThread().getName()
 				+ " Sale : " + ticket--);
 			}			
 		}
 	}
 }
 
 public class TicketRunDemo
 {
 	public static void main(String[] args)
 	{
 		TicketRun rd = new TicketRun();
 
 		Thread t1 = new Thread(rd);//        ,         run  
 		Thread t2 = new Thread(rd);
 	
 
 		t1.start();
 		t2.start();
 	
 
 	}
 }
 

 
実行結果(一部切り取り)
 
.........................
Thread-3 Sale : 46
 Thread-1 Sale : 47
 Thread-3 Sale : 42
 Thread-0 Sale : 43
 Thread-2 Sale : 44
 Thread-0 Sale : 39
 Thread-3 Sale : 40
 Thread-1 Sale : 41
 Thread-3 Sale : 36
 Thread-3 Sale : 34
 Thread-0 Sale : 37
 Thread-2 Sale : 38
 Thread-0 Sale : 32
 Thread-3 Sale : 33
.............................
 
では、両者の違いは何ですか.
両者のスレッドコードの格納場所が異なる:インタフェースを実現するスレッドコードはインタフェースのサブクラスrunメソッドに格納され、継承方式のスレッドコードはThreadサブクラスのrunメソッドに格納される
実装インタフェース方式は単一継承の限界を回避し、スレッドを定義する際に、実装インタフェース方式を提案する.
クラスがThreadを継承する場合、リソース共有には適していません.しかしRunableインタフェースを実現すれば,容易にリソース共有を実現できる.
 
Runnableインタフェースを実装することは、Threadクラスを継承することよりも優れています.
1):同じプログラムコードを複数適用したスレッドが同じリソースを処理する
2):javaでの単一継承の制限を回避できます.
3):プログラムの堅牢性を高め、コードは複数のスレッドで共有でき、コードとデータは独立している.
 
 
マルチスレッドのセキュリティの問題(同期とデッドロック)
 
複数の文が同じスレッドの共有データを操作する場合、1つのスレッドの複数の文は一部しか実行されず、まだ実行が完了していないため、別のスレッドが実行に関与し、共有データのエラーを招く.
解決方法:コードブロックを同期する.synchronized.オブジェクトはロックのようなもので、ロックを持つスレッドは再同期コードで実行することができ、ロックがないスレッドは、cpu実行権を取得しても同期コードブロックには入らない.
共有データを操作する文を同期コードブロックに入れる原理:フラグビットの設定(ロック)
同期の前提:
1.複数のスレッドが必要で、単一のスレッドは複数の同期を必要としない.
2.複数のスレッドは同一のロックを使用する必要がある.
同期中に1つのスレッドしか実行できないことを保証する必要があります.
 
利点:マルチスレッドのセキュリティ問題の解決
弊害:毎回ロック判断を行わなければならず、資源を消費する.
同期を採用すれば、同期コードブロックと同期方法の2つを使用して完了することができます.
【同期コードブロック】:
構文の形式:
synchronized(同期オブジェクト){
//同期が必要なコード
}
while (true)
 {
 	synchronized (this)
 	{
 		if (ticket > 0)
 		{
 
 			try
 			{
 				Thread.sleep(10);
 			} catch (Exception e)
 			{
 				System.out.println("error");
 			}
 			System.out.println(Thread.currentThread().getName()
 						+ " --code-- : " + ticket--);
 		}
 
 	}
}
 
【同期方法】
 
同期方法を採用することもできます.
構文フォーマットsynchronizedメソッドはタイプメソッド名(パラメータリスト)を返します{
//その他コード
}
 
public synchronized void show()	
{
 
 	if (ticket > 0)
 	{
 
 		try
 		{
 			Thread.sleep(10);
 		} catch (Exception e)
 		{
 			System.out.println("error");
 		}
 			System.out.println(Thread.currentThread().getName() + " ----show---- : "
 					+ ticket--);
 	}
 
 }
 
注意すべき点:
 
同期メソッドで使用されるロック
メソッドはオブジェクトに呼び出される必要があります.メソッドには属するオブジェクトの参照があります.これはthisです.同期方法で使用するロックはthisです
同期方法がstaticで修飾すると.ロックがClassオブジェクトの場合、使用するロックはクラス名である.class
静的メモリ入力時、メモリには本クラスのオブジェクトはありませんが、そのクラスのバイトコードオブジェクトがあります.
 
複数のスレッドが1つのリソースを共有する場合は同期が必要ですが、同期が多すぎるとデッドロックになる可能性があります.
具体例:
 
 
 public class ThisLockDemo
 {
 	public static void main(String[] args)
 	{
 		TicketRun rd = new TicketRun();
 
 		Thread t1 = new Thread(rd);//        ,         run  
 		Thread t2 = new Thread(rd);
 
 		t1.start();
 		try
 		{
 			Thread.sleep(20);
 		} catch (Exception e)
 		{
 		}
 		rd.flag = false;
 		t2.start();
 	}
 
 }
 
 class TicketRun implements Runnable
 {
 	private int ticket = 100;
 
 	boolean flag = true;
 	//Object o = new Object();
 
 	public void run()
 	{
 		if (flag)
 		{
 			while (true)
 			{
 				synchronized (this)
 				{
 					if (ticket > 0)
 					{
 
 						try
 						{
 							Thread.sleep(10);
 						} catch (Exception e)
 						{
 							System.out.println("error");
 						}
 						System.out.println(Thread.currentThread().getName()
 								+ " --code-- : " + ticket--);
 					}
 
 				}
 			}
 		} else
 			while (true)
 				show();
 	}
 
 	public synchronized void show()	//      static  .  Class  ,      	  .class
 	{
 
 		if (ticket > 0)
 		{
 
 			try
 			{
 				Thread.sleep(10);
 			} catch (Exception e)
 			{
 				System.out.println("error");
 			}
 			System.out.println(Thread.currentThread().getName() + " ----show---- : "
 					+ ticket--);
 		}
 
 	}
 }
 
 
 
エピソード:シングルデザインモード
 
餓漢式と怠け者式
餓漢式:
 
class Single
 {	
 	private static final Single s = new Single();
 	
 	private Single(){}
 	
 	public static Single getInstance()
 	{
 		return s;
 	}
 }
 
怠け者式:(例の遅延ロード)--->単例を保証できず、安全問題がある.
 
class Single
 {
 	private static Single s = null;
 	
 	private Single(){}
 	
 	public static Single getInstance() 
 	{
 		if(s == null)
 			s = new Single();
 		return s;
 	}
 }

 
強化された怠け者式:
 
public class Single
 {
 	private static Single s = null;
 	private Single() {}
 	
 	public static Single getInstance()
 	{
 		if(s == null)
 		{
 			synchronized(Single.class)
 			{
 				if(s == null)
 					s = new Single();
 			}
 		}
 		return null;
 	}
 }

 
 
Done...