blog

アーキテクトになりたいのであれば、これらのJavaマルチスレッドの知識を知らなければならない!

スレッドはプログラムの基本的な実行単位です。オペレーティングシステムがプログラムを実行すると、システム内にプロセスが作成されます。このプロセスでは、プログラムのメインエントリポイントとして機能するよう...

Jul 17, 2020 · 8 min. read
シェア

スレッディングの基本

スレッドの概要:スレッドはプログラムの基本的な実行単位です。オペレーティングシステムがプログラムを実行するとき、システム内にプロセスを作成します。このプロセスでは、プログラムのメインエントリポイントとして機能するスレッドを作成する必要があります。プロセスとスレッドは、オペレーティングシステムの本質的な動作モデルです。オペレーティングシステムでは、システムプロセスとユーザープロセスを含む複数のプロセスが存在することができ、プロセスは複数のスレッドを持つことができます。プロセスは複数のスレッドを持つことができます。 プロセスとプロセスは互いにメモリを共有できませんが、スレッドはプロセスのメモリを共有できるだけでなく、スレッドスタックとも呼ばれる独自のメモリ空間を持つことができ、主にスレッドが実行する関数で定義された変数など、スレッド内で使用されるデータを保存するために使用されます。スレッドの意義: CPUリソースのフル活用 プログラミングモデルの簡素化 非同期イベントの処理の簡素化 GUIの効率化 プログラムの実行効率の向上

スレッドの作成

Java言語では、スレッドを表現するためにTheradクラスを使用します。すべてのスレッドオブジェクトは、Theradクラスまたはそのサブクラスのインスタンスでなければなりません。Runnableインターフェイスを使用する方法です。

Theradクラスによるスレッドの作成

//スレッドの作成に必要なクラス.最初の方法は、スレッド・クラスを継承し、runメソッドをオーバーライドすることだ。
class Xc extends Thread {
	public void run() {//runメソッドを書き直す
		for(int i=0;i<10;i++) {
			System.out.println("スレッド関数"+i);	
		}
	}
}
public class mythread{
	public static void main(String[] args) {
	Xc xc1=new Xc();// 
	xc1.start();//スレッド1の開始
	Xc xc2=new Xc();// 
	xc2.start();//スレッド2の開始
	}
}

結果

スレッド・クラスのサブクラスはインスタンス化できますが、スレッドのコードを実行するには、サブクラスで run() メソッドをオーバーライドする必要があります。

//つ目の方法は、runnableインターフェイスを実装し、run()メソッドを実装してスレッド・オブジェクトを作成し、startメソッドを呼び出す方法である。
class Xc1 implements Runnable{
	public void run() {
		for(int i=0;i<=10;i++) {
			System.out.println("スレッド関数"+i);	
		}
	}
}
public class mythread01 {
	public static void main(String[] args) {
		Xc1 a1 =new Xc1();
		Xc1 a2 =new Xc1();
		Thread a= new Thread(a1);
		Thread b= new Thread(a2);//インスタンス化されたオブジェクトをスレッドaに渡す,b
		a.start();
		b.start();
	}
}

結果

これは、2つのスレッドが同時に実行されていることを示しています。 Runnableインターフェイスを使用するクラスを使ってスレッドを作成するには、Theradクラスのインスタンスを使用する必要があります。 Runnableインターフェイスを使用してスレッドを作成するには2つのステップがあります:Runnableインターフェイスをインスタンス化し、Theradオブジェクトを作成し、最初のステップでインスタンス化されたオブジェクトをパラメータとしてTheradクラスのコンストラクタ・メソッドに渡し、最後にTherad クラスの start() メソッドでスレッドを実行します。

III.一般的なねじ切り方法

currentTherad(): Theradクラスの静的メソッドで、現在実行中のスレッドオブジェクトを返します setName(); スレッド名を設定 getName(); スレッド名を取得 isAlive(); スレッドが実行中かどうか、実行中の場合はtrueを返し、そうでない場合はfalseを返します。

public class MyThread03 extends Thread{
	public void run(){
		System.out.println(this.getName());//スレッド名の取得
	}
	public static void main(String[] args) {
		System.out.println(Thread.currentThread().getName());//現在実行中のスレッドを取得する
		MyThread03 thread1=new MyThread03();
		System.out.println("isAlive:"+thread1.isAlive());//スレッドが実行中かどうかを返す
		MyThread03 thread2=new MyThread03();
		MyThread03 thread3=new MyThread03();//3つのスレッドを作成する
		thread1.setName(" ");
		thread2.setName(" ");//スレッド1のセットアップ.2 
		thread1.start();
		thread2.start();
		thread3.start();
	}
}

走行結果

最初の出力は、メイン関数のスレッド名であり、2行目は、スレッドスレッド1は、スレッド3は、スレッド名、すべてのシステムのデフォルト名スレッド-2されていないために、もはや実行状態にないことを示します。

糸のライフサイクル

スレッドは、開始、実行、ハング、停止の4つの異なる状態を経て、スレッドクラスのメソッドによって制御されます。スレッドが run() メソッドの実行を開始すると、スレッドが終了する前にメソッドが終了するまで実行し続けます。これは、スレッドを終了するメソッドですが、他のメソッドは、強制的にスレッドを終了stop()メソッドは、スレッドの使用後にサスペンド()、あなたが再開()メソッドを介してスレッドをウェイクアップすることができます(長いミリ)メソッドの使用は、スレッドを休止状態にするために、設定された時間の終了時にのみ準備の状態である、メソッドは、一般的に例外をスローすることです結合()。このメソッドは、他のスレッドの実行前にスレッドの終わりの実行は、スレッドの必須実行です。

public class MyThread04 extends Thread{
	public void run() {
		for(int x=0;x<5;x++) {
			try {
				Thread.sleep(600);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("サブスレッド番号:"+x);
		}
	}
	public static void main(String[] args) throws InterruptedException {
		Thread mainThread =Thread.currentThread();//メイン・スレッドを取得する
		MyThread04 thread =new MyThread04();
		thread.start();//サブスレッドの開始
		
		for(int x=0;x<10;x++) {
			if(x==5) { //実行条件を設定する
				try {
					mainThread.join();//スレッドタスクを強制する
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
			}
			try {
				Thread.sleep(100);//遅延実行
				
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+" ,x="+x);
		}
	}
}

結果

この手続きは、メインスレッドとサブスレッドの2つのスレッドを開始し、もともと2つの手続きが交互に実行され、メインスレッドは、サブスレッドの完了を実行するために強制されるときにx = = 5を満たすときに実行し続ける、もともとメインスレッドが10回実行される、結合()メソッドが発生し、完了を実行することを余儀なくされました。

V. スレッドの同期とデッドロック

スレッドを使用するプログラムは、より効率的なプログラムの処理することができます、マルチスレッドプログラムがない場合は、メインスレッドがすべてで処理される特定のリソースの処理でプログラムが、この処理速度は非常に遅くなりますが、マルチスレッド処理機構を使用する場合は、リソースの処理と一緒にサブスレッドとメインスレッドを行うことができますし、スレッドの実行効率は、シングルスレッドよりもはるかに高くなります。リソースのマルチスレッド同時処理の使用は、シングルスレッドよりもはるかに効率的ですが、同じリソースのマルチスレッド操作は、リソースの整合性の問題の操作など、いくつかの問題がある必要があります、したがって、同期とデッドロックの問題のスレッドがあります。

スレッド同期 スレッド同期とは、複数のスレッドオブジェクトが並列にリソースアクセスを実行する場合に、リソース処理を可能にする保護操作です。 以下は、チケット購入をシミュレートするプログラムを使った同期問題の説明です。

class MyThread implements Runnable{//スレッド実行クラスを定義する
	private int ticket =3;//投票総数は3
	public void run() {
		while(true) {//チケットを買い続ける
			if(this.ticket>0) {//まだチケットが残っている
			try {
				Thread.sleep(100);//ネットワークの待ち時間をシミュレートする
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"チケットを買う="+this.ticket--);
			}else {
				System.out.println("****チケットは完売した!****");
			break;//ループから飛び出す
			}
		}
	}
}
public class MyThread05 {
	public static void main(String[] args) throws Exception{
	MyThread mt=new MyThread();
	new Thread(mt,"チケットマスターA").start();//チケットスレッドを開く
	new Thread(mt,"チケットマスターB").start();
	new Thread(mt,"チケットマスターC").start();
	}
}

結果

このプログラムでは、同期の問題をよりよく観察するために、投票数とチケット販売操作の間にスレッドのスリープ操作を追加し、遅延をシミュレートしています。実行結果からも、プログラムが同期していないことがわかります。これは主にコードの構造に起因するもので、チケット販売操作が2つのステップに分かれているためです。

ステップ1:チケットの枚数が0より多いかどうかを判断します。

ステップ2:投票数が0を超えたら、チケットを販売します。

最後の1枚しか残っていないと仮定すると、最初のスレッドがチケットの売却条件を満たしたとき、他のスレッドも同時にチケットの売却条件を満たす可能性があるため、同時の自己減算操作によってマイナスの数字になる可能性があります。

スレッド同期

並行資源へのアクセスが非同期化される主な理由は、複数のプログラム・ロジック・ユニットの全体的なロックがないこと、つまり、データの判断や変更を行う際に、1つのスレッドにのみ処理が許可され、他のスレッドは現在のスレッドの実行終了を待ってから実行を継続する必要があるため、同じ期間内に1つのスレッドのみに操作を許可することで同期処理を実現することができます。

Javaは同期処理を実現するためにsynchronizedキーワードを提供し、同期化の鍵は、コードに "ロック "を追加することであり、ロックプロシージャの操作のために2種類があります:同期コードブロック、同期メソッド。

同期コードブロックはsynchronizedキーワードを使用して定義され、コードの実行では、多くの場合、スレッド操作の不確実性のために、同期オブジェクトを設定する必要があるので、同期オブジェクトは、この時間は thisを選択することができます。

class MyThread implements Runnable{//スレッド実行クラスを定義する
	private int ticket =3;//投票総数は3
	public void run() {
		while(true) {//チケットを買い続ける
			synchronized(this) {
				if(this.ticket>0) {//まだチケットが残っている
					try {
						Thread.sleep(100);//ネットワークの待ち時間をシミュレートする
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"チケットを買う="+this.ticket--);
				}else {
					System.out.println("****チケットは完売した!****");
					break;//ループから飛び出す
				}
			}
		}
	}
}
public class MyThread05 {
	public static void main(String[] args) throws Exception{
	MyThread mt=new MyThread();
	new Thread(mt,"チケットマスターA").start();//チケットスレッドを開く
	new Thread(mt,"チケットマスターB").start();
	new Thread(mt,"チケットマスターC").start();
	}
}

走行結果

このプログラムでは、投票数判定と投票数自己減少の2つの制御ロジックを同期コードブロックにまとめ、複数のスレッドが同時に実行する場合には、この部分を1つのスレッドにのみ実行させることで、同期処理動作を実現しています。

ヒント: 同期は処理性能の低下を引き起こす可能性があります。

同期の本質は、一度に実行できるのは1つのスレッドだけなので、スレッド・オブジェクトが終了していない間、他のスレッドは待機状態になり、処理性能の低下につながります。しかし、同期にはいくつかの利点もあります。スレッドによるデータへのアクセスは安全です。

Read next