Javaスレッドの同期化(Synchronized)トピック


1.1つのオブジェクトが持つデータがマルチスレッドで同時に共有アクセスできる場合、データ同期の問題を考慮する必要があります.データ同期とは、2つのデータの全体性と一貫性を指す.データがマルチスレッドで共有されると、同時に複数のスレッドが同じオブジェクトの情報を更新する可能性があるため、オブジェクトデータの不同期が発生しやすくなります.データの不同期によるエラーは通常気づきにくく、プログラムが数千数万回実行された後にエラーが発生する可能性があります.これは、通常、製品がオンラインになった後、プログラムが実行されてから数年後に発生します.
2.簡単な例を挙げると、PersonalInfoクラスが設計されています.
package ysu.hxy;

public class PersonalInfo 
{
	private String name;
	private String id;
	private int count;

	public PersonalInfo()
	{
		name = "nobody";
		id = "N/A";
	}

	public void setNameAndID(String name,String id)
	{
		this.name = name;
		this.id = id;
		if(!checkNameAndIDEqual())
		{
             System.out.println(count + ") illegal name or ID...");
		}
		count ++;
	}

	private boolean checkNameAndIDEqual(){
		return (name.charAt(0) == id.charAt(0)) ? true:false;
	}
}

このクラス自体ではエラーはありませんが、マルチスレッドのプログラムで使用され、同じオブジェクトが複数のスレッドにアクセスされている場合、エラーが発生する可能性があります.次は簡単なテストプログラムで、PersonalInfoクラスがマルチスレッド共有データの下でどのような問題が発生するかを見てみましょう.
package ysu.hxy;

public class PersonalInfoTest 
{
	public static void main(String[] args) 
	{
		final PersonalInfo person = new PersonalInfo();

		//            person   
		Thread thread1 = new Thread(new Runnable() {
			public void run() {
				while(true){
					person.setNameAndID("Justin Lin","J.L");
				}
			}
		});

        Thread thread2 = new Thread(new Runnable() {
			public void run() {
				while(true){
					person.setNameAndID("Shang Hwang Lin","S.H");
				}
			}
		});

		System.out.println();

		thread1.start();
		thread2.start();
	}
}

 
実行結果:
D:\hxy>java ysu.hxy.PersonalInfoTestがテストを開始...23466451) illegal name or ID...78044494) illegal name or ID...101630476) illegal name or ID...106496643) illegal name or ID...145330181) illegal name or ID...169674022) illegal name or ID...174072203) illegal name or ID...214717201) illegal name or ID...219668799) illegal name or ID...240921750) illegal name or ID...265875722) illegal name or ID...270920923) illegal name or ID...281256783) illegal name or ID...
このプログラムにエラーが発生し、23466451回のsetNameAndID()実行時に開始しました.プログラムが完了し、実際の場面に適用され始めた場合、この時点は数ヶ月から数年後になる可能性があります.問題は次のとおりです.
public void setNameAndID(String name,String id)
	{
		this.name = name;
		this.id = id;
		if(!checkNameAndIDEqual())
		{
             System.out.println(count + ") illegal name or ID...");
		}
		count ++;
	}

setNameAndID()に渡される変数は問題ありませんが、ある時点でthread 1はJustin Lin、J.Lはname、idを設定し、ifテストを行う直前にthread 2がsetNameAndID(「Shang Hwang」,「S.H」)を呼び出す可能性があります.名前がShang HWangに設定されている場合、checkName AndeIDEqual()が実行を開始します.名前はShang HWangに等しく、idはJ.Lです.したがって、checkName AndeIDEqual()はfalseを返し、エラーメッセージが表示されます.
オブジェクトの更新は、あるスレッドがpersonオブジェクトのデータを設定している場合に、別のスレッドで同時に設定できないように同期する必要があります.この動作はsynchronizedキーワードを用いて行うことができる.
public synchronized void setNameAndID(String name,String id)
	{
		this.name = name;
		this.id = id;
		if(!checkNameAndIDEqual())
		{
             System.out.println(count + ") illegal name or ID...");
		}
		count ++;
	}

これはsynchronizedキーワードの1つの使用方法であり,方法的に方法の範囲内を同期化された領域にするために用いられる.同期化された領域は、1つのスレッドが占有されている場合、他のスレッドが入ることを許可しないペナルティ領域のようです.同じ時間に同期化された領域に1つのスレッドしか存在しないため、共有データを更新すると、単一スレッドプログラムがデータを更新するように、オブジェクト内のデータが所定のデータと同期することを保証します.
Sychronizedの設定は、方法上だけでなく、あるプログラムブロック上の同期化された領域を限定するためにも用いることができる.例:
public void setNameAndID(String name,String id)
  { //        
       synchronized(this)
   {
       this.name = name;
       this.id = id;
       if(!checkNameAndIDEqual())
          {
               System.out.println(count+") illegal name or ID...");
          }
   }
}

このプログラムセグメントは、synchronizedによって設定された同期化されたブロックにスレッドが実行されると、現在のオブジェクトをロックし、この同期化されたブロックを実行する他のスレッドがないことを意味する.この方法は、メソッドブロック全体をロックしたくないのではなく、共有データを更新したときにオブジェクトとデータの同期化を確保したいだけです.メソッド内のブロックのみをロックするため、ブロックが実行されるとオブジェクトのロックが解除され、他のスレッドがメソッドブロック全体をロックするよりもオブジェクトを操作できるようになります.
オブジェクトの同期化が必要であることを示すこともできます.例えば、マルチスレッドにおいて同じArrayListオブジェクトにアクセスする場合、ArrayListはデータアクセス時の同期化を実現していないため、マルチスレッド環境を使用する場合、複数のスレッドが同じArrayListにアクセスする場合、2つ以上のスレッドがArrayListの同じ位置にデータを格納し、データが互いに上書きされる可能性があることに注意しなければならない.データ格納時の正確性を確保するため、ArrayListオブジェクトへのアクセス時に同期化を要求することができる.例:
//arraylist     ArrayList     
synchronized(arraylist)
{
     arrayList.add(new SomeClass());
}

同期化は、データの同期を確保しますが、1つのスレッドが同期化ブロックを占有し、他のスレッドがブロックの実行権を解放するのを待つ遅延が犠牲になります.これは、スレッドが少ない場合には見えないかもしれませんが、スレッドが多い環境では、大規模なWebサイトの複数のオンライン時など、一定の効率的な問題が発生します.