Javaマルチスレッド(3)---同期とデッドロック

質問の起源:

電車の切符の販売を例にとると、今すぐ電車の切符を購入したい場合は、駅やさまざまなオンラインの発券店に行くことができます。ただし、電車の切符を販売する場所がいくつあっても、電車の数はチケットは固定されています。すべてのチケット販売店は同じ列車のチケットを共有する必要があります。チケットポイントがスレッドに例えられる場合、すべてのスレッドは時間内にプロセスリソースを共有します。

コードの実装:

class MyThread1 implements Runnable{
	private int ticket=5;
	public void run() {
		for(int i=0;i<100;i++) {
			if(ticket>0) {
				System.out.println("卖第"+ticket--+"张票");}}}}
public class TongBu {
	public static void main(String[] args) {
		MyThread1 mThread1=new MyThread1();
		Thread t1=new Thread(mThread1);
		Thread t2=new Thread(mThread1);
		Thread t3=new Thread(mThread1);
		t1.start();
		t2.start();
		t3.start();}}

演算結果:
画像

しかし、実際のアプリケーションでは、データはデータベースに保存され、ユーザー注文情報もデータベースに保存されます。つまり、データをデータベースに保存するには一定の時間遅延が必要です。ここでは、ネットワーク遅延をシミュレートします。 。

実装コード:

class MyThread1 implements Runnable{
	private int ticket=5;
	public void run() {
		for(int i=0;i<100;i++) {
			if(ticket>0) {
				System.out.println("卖第"+ticket--+"张票");
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					// TODO: handle exception}}}}}
public class TongBu {
	public static void main(String[] args) {
		MyThread1 mThread1=new MyThread1();
		Thread t1=new Thread(mThread1);
		Thread t2=new Thread(mThread1);
		Thread t3=new Thread(mThread1);
		t1.start();
		t2.start();
		t3.start();}}

実行結果を見てみましょう:
画像

チケットの販売数が繰り返しており、プログラムコードに問題があり、ユーザー間の競合も発生していることがわかります。

なぜそのような現象があるのですか?

ここで、オペコードの実装手順を簡単に分析してみましょう。
ここに画像の説明を挿入

ここでは、スレッド1がリソース内のデータを取り出し、マイナス1の操作を実行してから、元に戻すことがわかります。ここで番号を繰り返す理由は、スレッド1がリソースを取り出すと、スレッド2もそれを取り出すためです。同時に、たとえば、スレッド1がそれを取り出します。前は3で、スレッド1が出た後の数は3でした。しかし、ネットワークの遅延のため、スレッド1が取り出されたばかりで、処理されずに戻されたとき、スレッド2ページは同時にリソース3を取り出しました。このとき、スレッド1とスレッド2は両方とも3のデータを取得しました。このとき、スレッド1は2として計算して2を出力し、スレッド2は2として計算して出力します。 2、繰り返し数が生成されるように。

この問題を解決する方法は?

このような問題を解決したい場合は、同期を使用する必要があります。いわゆる同期とは、複数の操作を同じ期間に1つのスレッドでのみ実行でき、他のスレッドはこのスレッドの完了を待ってから続行する必要があることを意味します。つまり、スレッド1が実行中、つまりスレッド1がデータをフェッチした後、スレッド2はリソース変数を操作できません。これは同期ロックです。

画像

同期する

リソース共有の同期操作の問題を解決するには、同期コードブロックと同期方法を使用して完了します。

同期コードブロック

コードブロックは、次の4種類のコードブロックに分けられます。

  • 共通コードブロック:メソッドで直接定義されたコードブロックです。
  • ビルディングブロック:クラスで直接定義され、構築メソッドの前に実行され、繰り返し呼び出すことができます。
  • 静的高速:staticキーワードを使用して宣言されます。これは、ビルディングブロックの実行よりも優先され、1回だけ実行されます。
  • 同期コードブロック:synchronizedキーワードで宣言されたコードブロックは、同期コードブロックと呼ばれます。

定義形式:

synchronize(同期オブジェクト){

同期してロックする必要のあるコード;}

一般に、現在のオブジェクトは同期されたオブジェクトと見なされ、これによって表されます。

public void run() {
		for(int i=0;i<100;i++) {
			synchronized (this) {			
			if(ticket>0) {				
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					// TODO: handle exception
				}
				System.out.println("卖第"+ticket--+"张票");
			}}

プログラムは段階的に実行されますが、速度は低下します。
同期方法

メソッドの戻り値の前に同期を追加します。

形式は、同期メソッドの戻り値メソッド名(パラメーターリスト){}です。

デッドロック

プログラムが多すぎるとデッドロックが発生します

デッドロックの前提条件は同期です。たとえば、2つのスレッドがあります。スレッドAはスレッドBのリソースを実行する必要があり、スレッドBもスレッドAのリソースを実行する必要があります。常に相手が取り出すのを待っています。デッドロックと呼ばれるリソース。

マルチスレッドとマルチプロセッシングは、システムリソースの使用率を向上させ、システムの処理能力を向上させます。ただし、同時実行は、デッドロックという新しい問題ももたらします。いわゆるデッドロックとは、複数のスレッドがリソースを奪い合うことによって引き起こされるデッドロック(相互に待機)を指します。外力がない場合、これらのプロセスは先に進むことができません。

いわゆるデッドロックとは、実行プロセス中のリソースの競合により、2つ以上のスレッドが互いに待機する現象を指します。外力がないと、スレッドは前進できません。

以下では、デッドロック現象を説明するために例を使用します。

二人が一枚板の橋を向かい合って渡ります。AとBの両方がすでに橋の上を歩いている、つまり橋の資源を占有しています。Aが一枚板の橋を通過したい場合、Bはブリッジを使用してブリッジリソースを解放し、Aを通過させますが、Bいいえ、Bはまだ最初に移動したいので、AとBの両方がブリッジでスタックし、これはデッドロックです。

画像

デッドロックの必要条件:

デッドロックの生成には4つの条件が不可欠です。これは、継承に基づいて多型を生成する必要があるのと同様です。

  • 相互に排他的な条件:プロセスには、割り当てられたリソース(プリンターなど)の排他的な制御が必要です。つまり、特定のリソースは、一定期間内に1つのプロセスのみが占有できます。この時点で他のプロセスがリソースを要求した場合、要求されたプロセスは待機することしかできません。
  • 非剥奪条件:プロセスによって取得されたリソースは、使用される前に他のプロセスによって強制的に奪われることはできません。つまり、リソースを取得したプロセスによってのみ解放できます(受動的ではなく能動的にのみ)。
  • リクエストと保持の条件:プロセスは少なくとも1つのリソースを保持していますが、新しいリソースリクエストを作成し、リソースは他のプロセスによって占有されています。この時点で、リクエストしているプロセスはブロックされていますが、取得したリソースはブロックされません。解放されます。

循環待機条件:プロセスリソースの循環待機チェーンがあり、チェーン内の各プロセスによって取得されたリソースは、チェーン内の次のプロセスによって同時に要求され、待機状態のプロセスのセットがあります:p1 、p2 ... pn}、Piを待機しているリソースはP(i + 1)(i = 0、1、…、n-1)によって占有され、Pnを待機しているリソースはP0によって占有されます。図1。

直感的には、ループ待機条件はデッドロックの定義と同じように見えますが、そうではありません。デッドロックの定義によれば、待機ループを形成するために必要な条件はより厳しく、Piが待機するリソースはP(i + 1)によって満たされる必要がありますが、循環待機条件にはそのような制限はありません。たとえば、システムには2つの出力デバイスがあります。P0が1つを占め、PKがもう1つを占め、Kはセット{0、1、…、n}に属していません。

  • Pnは出力デバイスを待機します。P0またはPKから取得できます。したがって、Pn、P0、およびその他のいくつかのプロセスは循環待機サークルを形成しますが、PKはサークル内にありません。PKが出力デバイスを解放すると、図2-16に示すように、循環待機が中断される可能性があります。したがって、循環待機はデッドロックの必要条件にすぎません。

画像

デッドロックを回避する方法は?

  • デッドロックを破壊する4つの主要な条件の1つ
  • ロックシーケンス(スレッドは特定の順序でロックされます)
  • ロック時間制限(スレッドがロックを取得しようとすると特定の時間制限が追加され、制限時間を超えると、ロックの要求が破棄され、所有しているロックが解放されます)
  • デッドロックの検出

おすすめ

転載: blog.csdn.net/qq_44762290/article/details/112379636