マルチスレッドの同期コードブロックとロック


前編のコードでは、マルチスレッドが同じリソースブロックに動作する可能性があるという問題があります.この問題を解決する方法は、同期コードが速いか、鍵をかけることです.ロックの役割は、複数のスレッドをプリエンプトすることなく、リソースの占有を常に1つのスレッドで呼び出すことです.
では、スレッドセキュリティの問題をどのように解決するかというと、まず同期コードが速いことです.
package com.qf.duoxian;

public class Ticket1 implements Runnable {

	public Ticket1() {
		// TODO Auto-generated constructor stub
	}

	int ticket = 100;
	
  Object
     lock = new Object();
	
	
	@Override
	public void run() {
		while (true){
			synchronized (lock) {
				if(ticket>0){
					try {
						Thread.sleep(1);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
						
					}
					System.out.println(Thread.currentThread().getName()+" "+ticket--);
				}
			}
		}
		
	}

}
class ThreadDemo2{
	public static void main(String[] args) {
		Ticket1 ticket1 = new Ticket1();
	    
		Thread t1 = new Thread(ticket1," a");
		Thread t2 = new Thread(ticket1," b");
		Thread t3 = new Thread(ticket1," c");
	
	    t1.start();
	    t2.start();
	    t3.start();
	}
}

印刷された結果です.
私に売って列车の切符a谁が买った切符100私に売って列车の切符a谁が买った切符99私に売って列车の切符a谁が买った切符98私に売って列车の切符a谁が买った切符97
そして同期方法です.
2つ目は鍵をかけることですLockインプリメンテーションは、synchronizedメソッドおよび文を用いて得られるよりも広範なロック動作を提供する.void lock()ロックを取得します.void unlock()ロックを解除します.
例:より簡単です.
public class Ticket2 implements Runnable {

	public Ticket2() {
		// TODO Auto-generated constructor stub
	}
    int  ticket = 100;
	
    Lock lock = 	new  ReentrantLock();
	@Override
	public void run() {
		
		while(true){
			lock.lock();
			if(ticket>0){
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+" "+ticket--);
            lock.unlock();
			}
		}
	}

}
class ThreadPool{
	public static void main(String[] args) {
		Ticket2 ticket = new Ticket2();
		
		Thread   = new Thread(ticket," 1");
		Thread  1 = new Thread(ticket," 2");
		
		Thread  2 = new Thread(ticket," 3");
		 .start();
		 1.start();
		 2.start();
	    
	}
}

デッドロック:
 
同期ロックの使用の弊害:スレッドタスクに複数の同期(複数のロック)が発生した場合、同期に他の同期がネストされている場合.この場合、プログラムに無限の待機が現れる現象を引き起こしやすい.この現象をデッドロックと呼ぶ.このような状況が避けられるなら避ける.
起動待ちメカニズム:
 
起動待ちメカニズムの説明を開始する前に、スレッド間の通信という概念を明らかにする必要があります.複数のスレッドは同じリソースを処理していますが、処理の動作(スレッドのタスク)は異なります.一定の手段で各スレッドがリソースを有効に利用できるようにする.このような手段は、起動を待つメカニズムである.
例えば、inputがスレッドで、outputがスレッドで、mainがメインスレッドで、inputがメンバー変数に値を付与し、outputがメンバー変数値を取得する機能を実現します.
しかし、1つの問題は、inputが1つのメンバー変数に値を付与した後、outputがメンバー変数の値を取得し始めたとき、まだ完全に取得されていないので、inputが次のメンバー変数に値を付与すると、outputが取得した値がinputが2回目にメンバー変数に付与した値である可能性があります.この方法を解決するには、inputとoutputの2つのスレッドに通信を実現させ、inputが値を付与した後、outputが値を取得するのを待つことを保証する必要があります.値を取得すると、inputに信号を与え、値を再付与します.乱数値が発生しないことを保証します.
 
起動待ちメカニズムに関する方法:
wait():実行中のスレッドから実行資格と実行権が解放され、スレッドプールに格納されるまで待機します.
notify():起動、起動スレッドプール内のwait()のスレッドを起動し、1回に1つ起動し、任意です.
notifyAll():すべてを起動:スレッドプール内のすべてのwait()スレッドを起動できます.
 
1.inputがResourceにデータがないことを発見した場合、入力を開始し、入力が完了したらoutputを呼び出して出力します.データが見つかったらwait();
2.outputがResourceにデータがないことを発見した場合、wait()データが見つかったら出力し、inputを起こしてデータを入力します.
public class Resources {

	private String name;
	private String sex;
	private boolean flag = false;
	public synchronized void  set(String name, String sex) {
	  if(flag)
		  try {
			wait();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		  this.name = name;
		  this.sex = sex;
		  
		  flag = true;
		  
		  this.notify();
	  
		
	}
	public synchronized void out(){
		if(!flag)
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(" :"+name+" :"+sex);
			flag = false;
			this.notify();
		
	}
	
	
	

}
public class Output implements Runnable {

	private Resources r;
	
	public Output(Resources r) {
		this.r = r;
	}

	@Override
	public void run() {
		while (true){
			r.out();
		}
		
	}

}
class ThreadDemo3{
	public static void main(String[] args) {
		Resources r = new Resources();
		Input in = new Input(r);
		Output out = new Output(r);
	
	    Thread t1 = new Thread(in);
	    Thread t2 = new Thread(out);
	
	    t1.start();
	    t2.start();
	}
}
public class Input implements Runnable {
    private Resources r;

	public Resources getR() {
		return r;
	}


	public Input(Resources r) {
		this.r = r;
	}


	public void setR(Resources r) {
		this.r = r;
	}

	@Override
	public void run() {
	int count = 0;
	while(true){
		if(count ==0){
			r.set(" ", " ");
		}else{
			r.set(" ", " ");
		}
		count = (count+1)%2;
	}
		
	}

}

スレッドプール
スレッドプールの概念:
スレッドプールは、複数のスレッドを格納するコンテナであり、スレッドを繰り返し使用することができ、スレッドオブジェクトを頻繁に作成する操作を省き、スレッドを繰り返し作成する必要がなく、リソースを消費しすぎます.
 
スレッドプールを使用する理由を詳しく説明します.
Javaでは、リクエストが到着するたびに新しいスレッドが作成される場合、オーバーヘッドはかなり大きいです.実際の使用では、スレッドの作成と破棄にかかる時間と消費されるシステムリソースは、実際のユーザーリクエストを処理する時間とリソースよりもはるかに大きい場合があります.スレッドの作成と破棄のオーバーヘッドに加えて、アクティブなスレッドもシステムリソースを消費する必要があります.1つのjvmにスレッドを作成しすぎると、メモリを消費しすぎたり、「切り替えすぎ」しすぎたりしてシステムリソースが不足する可能性があります.リソース不足を防止するためには、特定の時点で処理されるリクエストの数を制限し、スレッドの作成と破棄の回数をできるだけ減らし、特にリソースの消費が比較的大きいスレッドの作成と破棄を可能な限り減らし、既存のオブジェクトを利用してサービスを行う方法が必要です.
スレッドプールは、主にスレッドのライフサイクルオーバーヘッドの問題とリソース不足の問題を解決するために使用されます.複数のタスクに対してスレッドを繰り返し使用することで、スレッド作成のオーバーヘッドが複数のタスクに割り当てられ、リクエストが到着したときにスレッドがすでに存在するため、スレッド作成による遅延が解消されます.これにより、リクエスト・サービスのためにアプリケーション・レスポンスを迅速に使用できます.また,スレッド内のスレッド数を適切に調整することで,リソース不足を防止できる.
スレッドプールの使用方法:
1.Runnableインタフェース
2.ケーブルコネクタ