排他
日本語 | 他を排する |
英語 | exclusive |
ふりがな | はいた |
フリガナ | ハイタ |
ある処理をしている間、他のスレッドがその処理をできなくすること。
マルチスレッドで同期を取るために、あるスレッドが処理をしている間、他のスレッドが同一の処理をしようとした場合にそのスレッドを一時停止させたり処理を中止させることになる。
この「他のスレッドを止める」ことを「排他」といい、この仕組みを「排他処理」「排他制御」と言う。
詳しくは「同期」を参照。同期は目的であり、排他はその手段である。
マルチスレッドで同期を取るために、あるスレッドが処理をしている間、他のスレッドが同一の処理をしようとした場合にそのスレッドを一時停止させたり処理を中止させることになる。
この「他のスレッドを止める」ことを「排他」といい、この仕組みを「排他処理」「排他制御」と言う。
詳しくは「同期」を参照。同期は目的であり、排他はその手段である。
参考サイト
- (参考サイトはありません)
// Sample.java
public class Sample
{
public static void main( String[] args )
{
try
{
SynchronizeClass synchronizeClass = new SynchronizeClass();
// 別スレッドの方を呼び出します。
OtherThread thread = new OtherThread( synchronizeClass, "normalMethod" );
thread.start();
// 2秒待ちます。OtherThreadの方を先に
// 実行するためです。
Thread.sleep( 2 * 1000 );
// こちらでも。
System.out.println( "Sample開始" );
// 普通のメソッドを呼び出します。
synchronizeClass.normalMethod( "Sample" );
System.out.println( "Sample終了" );
// OtherThread開始
// normalMethod()メソッド開始 [0] from OtherThread
// Sample開始
// normalMethod()メソッド終了 [100] from OtherThread
// normalMethod()メソッド開始 [100] from Sample
// OtherThread終了
// normalMethod()メソッド終了 [200] from Sample
// Sample終了
// このように、普通にマルチスレッドで共通のフィールドに
// アクセスすると、値がうまくとれない場合があります。
// 「値を取得して書き換える処理」は、この
// 「値の取得」と「値の書き換え」を1つのセットとし、
// この処理中に他のスレッドが同じ処理をしないように
// する必要があります。
// つまり、この「値を取得して書き換える処理」を
// 行っている間、他のスレッドを処理させないようにする
// ことが「排他処理」です。
// というわけで、synchronizedメソッドで
// 排他します。
synchronizeClass = new SynchronizeClass();
thread = new OtherThread( synchronizeClass, "synchronizedMethod" );
thread.start();
// 2秒待ちます。OtherThreadの方を先に
// 実行するためです。
Thread.sleep( 2 * 1000 );
// こちらでも。
System.out.println( "Sample開始" );
// 普通のメソッドを呼び出します。
synchronizeClass.synchronizedMethod( "Sample" );
System.out.println( "Sample終了" );
// OtherThread開始
// synchronizedMethod()メソッド開始 [0] from OtherThread
// Sample開始
// synchronizedMethod()メソッド終了 [100] from OtherThread
// synchronizedMethod()メソッド開始 [100] from Sample
// OtherThread終了
// synchronizedMethod()メソッド終了 [200] from Sample
// Sample終了
// このように、synchronizedメソッドは、同じthisの場合は
// 呼び出せるのが1メソッドに限られます。そのため、
// 「値を取得して書き換える処理」を1セットとし、この
// セットが完了しない限り他のスレッドがこの処理を開始できない
// ようになっています。
// これが「排他処理」「排他制御」と呼ばれるものです。
}
catch( InterruptedException e )
{
// sleep()メソッドが途中で中断されると
// InterruptedException例外が投げられます。
// 滅多にないですが。
e.printStackTrace();
}
}
}
/**
* 別スレッドとして実行するためのクラス。
*/
class OtherThread extends Thread
{
/** SynchronizeClassクラス。 */
private SynchronizeClass synchronizeClass;
/** 呼び出すメソッド。 */
private String methodName = "";
/** コンストラクタ。 */
public OtherThread( SynchronizeClass synchronizeClass, String methodName )
{
this.synchronizeClass = synchronizeClass;
this.methodName = methodName;
}
/**
* Threadクラスのrun()メソッドを
* オーバーライドしたメソッド。このメソッドが
* 別スレッドとして呼び出されます。
*/
public void run()
{
System.out.println( "OtherThread開始" );
// メソッドを呼び出します。
if( "normalMethod".equals( methodName ) )
{
synchronizeClass.normalMethod( "OtherThread" );
}
else
{
synchronizeClass.synchronizedMethod( "OtherThread" );
}
System.out.println( "OtherThread終了" );
}
}
/**
* 同期処理テスト用クラス。
*/
class SynchronizeClass
{
/** フィールド。 */
private int data = 0;
/**
* 普通のメソッド。
*/
public synchronized void normalMethod( String name )
{
try
{
System.out.println( "normalMethod()メソッド開始 [" + data + "] from " + name );
// dataの中身を取得します。
int i = data;
// 5秒待ちます。
Thread.sleep( 5 * 1000 );
// その値に100を加えてdataにセットします。
data = i + 100;
System.out.println( "normalMethod()メソッド終了 [" + data + "] from " + name );
}
catch( InterruptedException e )
{
// sleep()メソッドが途中で中断されると
// InterruptedException例外が投げられます。
// 滅多にないですが。
e.printStackTrace();
}
}
/**
* synchronizedメソッド。
*/
public synchronized void synchronizedMethod( String name )
{
try
{
System.out.println( "synchronizedMethod()メソッド開始 [" + data + "] from " + name );
// dataの中身を取得します。
int i = data;
// 5秒待ちます。
Thread.sleep( 5 * 1000 );
// その値に100を加えてdataにセットします。
data = i + 100;
System.out.println( "synchronizedMethod()メソッド終了 [" + data + "] from " + name );
}
catch( InterruptedException e )
{
// sleep()メソッドが途中で中断されると
// InterruptedException例外が投げられます。
// 滅多にないですが。
e.printStackTrace();
}
}
}
/**
* データクラス。
*/
class DataClass
{
/**
* privateなフィールド。
*/
private int data;
/**
* getter。
*/
public int getData()
{
return data;
}
/**
* setter。
*/
public void setData( int value )
{
this.data = value;
}
}
public class Sample
{
public static void main( String[] args )
{
try
{
SynchronizeClass synchronizeClass = new SynchronizeClass();
// 別スレッドの方を呼び出します。
OtherThread thread = new OtherThread( synchronizeClass, "normalMethod" );
thread.start();
// 2秒待ちます。OtherThreadの方を先に
// 実行するためです。
Thread.sleep( 2 * 1000 );
// こちらでも。
System.out.println( "Sample開始" );
// 普通のメソッドを呼び出します。
synchronizeClass.normalMethod( "Sample" );
System.out.println( "Sample終了" );
// OtherThread開始
// normalMethod()メソッド開始 [0] from OtherThread
// Sample開始
// normalMethod()メソッド終了 [100] from OtherThread
// normalMethod()メソッド開始 [100] from Sample
// OtherThread終了
// normalMethod()メソッド終了 [200] from Sample
// Sample終了
// このように、普通にマルチスレッドで共通のフィールドに
// アクセスすると、値がうまくとれない場合があります。
// 「値を取得して書き換える処理」は、この
// 「値の取得」と「値の書き換え」を1つのセットとし、
// この処理中に他のスレッドが同じ処理をしないように
// する必要があります。
// つまり、この「値を取得して書き換える処理」を
// 行っている間、他のスレッドを処理させないようにする
// ことが「排他処理」です。
// というわけで、synchronizedメソッドで
// 排他します。
synchronizeClass = new SynchronizeClass();
thread = new OtherThread( synchronizeClass, "synchronizedMethod" );
thread.start();
// 2秒待ちます。OtherThreadの方を先に
// 実行するためです。
Thread.sleep( 2 * 1000 );
// こちらでも。
System.out.println( "Sample開始" );
// 普通のメソッドを呼び出します。
synchronizeClass.synchronizedMethod( "Sample" );
System.out.println( "Sample終了" );
// OtherThread開始
// synchronizedMethod()メソッド開始 [0] from OtherThread
// Sample開始
// synchronizedMethod()メソッド終了 [100] from OtherThread
// synchronizedMethod()メソッド開始 [100] from Sample
// OtherThread終了
// synchronizedMethod()メソッド終了 [200] from Sample
// Sample終了
// このように、synchronizedメソッドは、同じthisの場合は
// 呼び出せるのが1メソッドに限られます。そのため、
// 「値を取得して書き換える処理」を1セットとし、この
// セットが完了しない限り他のスレッドがこの処理を開始できない
// ようになっています。
// これが「排他処理」「排他制御」と呼ばれるものです。
}
catch( InterruptedException e )
{
// sleep()メソッドが途中で中断されると
// InterruptedException例外が投げられます。
// 滅多にないですが。
e.printStackTrace();
}
}
}
/**
* 別スレッドとして実行するためのクラス。
*/
class OtherThread extends Thread
{
/** SynchronizeClassクラス。 */
private SynchronizeClass synchronizeClass;
/** 呼び出すメソッド。 */
private String methodName = "";
/** コンストラクタ。 */
public OtherThread( SynchronizeClass synchronizeClass, String methodName )
{
this.synchronizeClass = synchronizeClass;
this.methodName = methodName;
}
/**
* Threadクラスのrun()メソッドを
* オーバーライドしたメソッド。このメソッドが
* 別スレッドとして呼び出されます。
*/
public void run()
{
System.out.println( "OtherThread開始" );
// メソッドを呼び出します。
if( "normalMethod".equals( methodName ) )
{
synchronizeClass.normalMethod( "OtherThread" );
}
else
{
synchronizeClass.synchronizedMethod( "OtherThread" );
}
System.out.println( "OtherThread終了" );
}
}
/**
* 同期処理テスト用クラス。
*/
class SynchronizeClass
{
/** フィールド。 */
private int data = 0;
/**
* 普通のメソッド。
*/
public synchronized void normalMethod( String name )
{
try
{
System.out.println( "normalMethod()メソッド開始 [" + data + "] from " + name );
// dataの中身を取得します。
int i = data;
// 5秒待ちます。
Thread.sleep( 5 * 1000 );
// その値に100を加えてdataにセットします。
data = i + 100;
System.out.println( "normalMethod()メソッド終了 [" + data + "] from " + name );
}
catch( InterruptedException e )
{
// sleep()メソッドが途中で中断されると
// InterruptedException例外が投げられます。
// 滅多にないですが。
e.printStackTrace();
}
}
/**
* synchronizedメソッド。
*/
public synchronized void synchronizedMethod( String name )
{
try
{
System.out.println( "synchronizedMethod()メソッド開始 [" + data + "] from " + name );
// dataの中身を取得します。
int i = data;
// 5秒待ちます。
Thread.sleep( 5 * 1000 );
// その値に100を加えてdataにセットします。
data = i + 100;
System.out.println( "synchronizedMethod()メソッド終了 [" + data + "] from " + name );
}
catch( InterruptedException e )
{
// sleep()メソッドが途中で中断されると
// InterruptedException例外が投げられます。
// 滅多にないですが。
e.printStackTrace();
}
}
}
/**
* データクラス。
*/
class DataClass
{
/**
* privateなフィールド。
*/
private int data;
/**
* getter。
*/
public int getData()
{
return data;
}
/**
* setter。
*/
public void setData( int value )
{
this.data = value;
}
}
// Sample.java public class Sample { public static void main( String[] args ) { try { SynchronizeClass synchronizeClass = new SynchronizeClass(); // 別スレッドの方を呼び出します。 OtherThread thread = new OtherThread( synchronizeClass, "normalMethod" ); thread.start(); // 2秒待ちます。OtherThreadの方を先に // 実行するためです。 Thread.sleep( 2 * 1000 ); // こちらでも。 System.out.println( "Sample開始" ); // 普通のメソッドを呼び出します。 synchronizeClass.normalMethod( "Sample" ); System.out.println( "Sample終了" ); // OtherThread開始 // normalMethod()メソッド開始 [0] from OtherThread // Sample開始 // normalMethod()メソッド終了 [100] from OtherThread // normalMethod()メソッド開始 [100] from Sample // OtherThread終了 // normalMethod()メソッド終了 [200] from Sample // Sample終了 // このように、普通にマルチスレッドで共通のフィールドに // アクセスすると、値がうまくとれない場合があります。 // 「値を取得して書き換える処理」は、この // 「値の取得」と「値の書き換え」を1つのセットとし、 // この処理中に他のスレッドが同じ処理をしないように // する必要があります。 // つまり、この「値を取得して書き換える処理」を // 行っている間、他のスレッドを処理させないようにする // ことが「排他処理」です。 // というわけで、synchronizedメソッドで // 排他します。 synchronizeClass = new SynchronizeClass(); thread = new OtherThread( synchronizeClass, "synchronizedMethod" ); thread.start(); // 2秒待ちます。OtherThreadの方を先に // 実行するためです。 Thread.sleep( 2 * 1000 ); // こちらでも。 System.out.println( "Sample開始" ); // 普通のメソッドを呼び出します。 synchronizeClass.synchronizedMethod( "Sample" ); System.out.println( "Sample終了" ); // OtherThread開始 // synchronizedMethod()メソッド開始 [0] from OtherThread // Sample開始 // synchronizedMethod()メソッド終了 [100] from OtherThread // synchronizedMethod()メソッド開始 [100] from Sample // OtherThread終了 // synchronizedMethod()メソッド終了 [200] from Sample // Sample終了 // このように、synchronizedメソッドは、同じthisの場合は // 呼び出せるのが1メソッドに限られます。そのため、 // 「値を取得して書き換える処理」を1セットとし、この // セットが完了しない限り他のスレッドがこの処理を開始できない // ようになっています。 // これが「排他処理」「排他制御」と呼ばれるものです。 } catch( InterruptedException e ) { // sleep()メソッドが途中で中断されると // InterruptedException例外が投げられます。 // 滅多にないですが。 e.printStackTrace(); } } } /** * 別スレッドとして実行するためのクラス。 */ class OtherThread extends Thread { /** SynchronizeClassクラス。 */ private SynchronizeClass synchronizeClass; /** 呼び出すメソッド。 */ private String methodName = ""; /** コンストラクタ。 */ public OtherThread( SynchronizeClass synchronizeClass, String methodName ) { this.synchronizeClass = synchronizeClass; this.methodName = methodName; } /** * Threadクラスのrun()メソッドを * オーバーライドしたメソッド。このメソッドが * 別スレッドとして呼び出されます。 */ public void run() { System.out.println( "OtherThread開始" ); // メソッドを呼び出します。 if( "normalMethod".equals( methodName ) ) { synchronizeClass.normalMethod( "OtherThread" ); } else { synchronizeClass.synchronizedMethod( "OtherThread" ); } System.out.println( "OtherThread終了" ); } } /** * 同期処理テスト用クラス。 */ class SynchronizeClass { /** フィールド。 */ private int data = 0; /** * 普通のメソッド。 */ public synchronized void normalMethod( String name ) { try { System.out.println( "normalMethod()メソッド開始 [" + data + "] from " + name ); // dataの中身を取得します。 int i = data; // 5秒待ちます。 Thread.sleep( 5 * 1000 ); // その値に100を加えてdataにセットします。 data = i + 100; System.out.println( "normalMethod()メソッド終了 [" + data + "] from " + name ); } catch( InterruptedException e ) { // sleep()メソッドが途中で中断されると // InterruptedException例外が投げられます。 // 滅多にないですが。 e.printStackTrace(); } } /** * synchronizedメソッド。 */ public synchronized void synchronizedMethod( String name ) { try { System.out.println( "synchronizedMethod()メソッド開始 [" + data + "] from " + name ); // dataの中身を取得します。 int i = data; // 5秒待ちます。 Thread.sleep( 5 * 1000 ); // その値に100を加えてdataにセットします。 data = i + 100; System.out.println( "synchronizedMethod()メソッド終了 [" + data + "] from " + name ); } catch( InterruptedException e ) { // sleep()メソッドが途中で中断されると // InterruptedException例外が投げられます。 // 滅多にないですが。 e.printStackTrace(); } } } /** * データクラス。 */ class DataClass { /** * privateなフィールド。 */ private int data; /** * getter。 */ public int getData() { return data; } /** * setter。 */ public void setData( int value ) { this.data = value; } }