synchronizedメソッド
日本語 | 同期関数 |
英語 | synchronized method |
ふりがな | しんくろないずどめそっど |
フリガナ | シンクロナイズドメソッド |
同期を取るメソッド。
synchronizedで修飾されたメソッド。「synchronized 戻り値 メソッド名(引数){ 実装 }」のように、メソッドの宣言の先頭にsynchronizedが付けられたメソッド。
マルチスレッドにおいて、複数のスレッドが同時に呼び出すことができないメソッド。
synchronizedメソッドは、呼び出すスレッドを1つに限定することができる。2つのスレッドからほぼ同時に呼び出された場合、先に呼び出した側がメソッドを終了させない限り、後から呼び出した側が待たされる。
ただし、synchronizedメソッドは、そのメソッドが属するインスタンスが異なる場合には機能しない。つまりsynchronizedメソッドは、thisが同じ場合にのみ待たせることができ、thisが異なる場合には待たせない。
これは、synchronizedメソッドが「フィールドの同期を取る」ためのものだからである。複数のスレッドが同時にアクセスすると、先のスレッドがフィールドの値を書き換える前に後のスレッドがフィールドの値を読み取り、値が誤ったものになってしまう可能性があるからである。
synchronizedメソッドはそれを防ぐためのものである。フィールドにアクセスするメソッドをsynchronizedメソッドとすることで、先のスレッドがメソッドを呼び出し終えるまで後のスレッドを待たせることができる。それにより、フィールドの値を常に保つことができる。
インスタンスが異なれば、フィールドも異なるため、同期を取る必要はない。インスタンスが異なるとsynchronizedメソッドが機能しないのはこのためである。
また、この目的のため、synchronizedメソッドは全て同期の対象となる。
synchronizedメソッドが複数存在する場合、同じように機能する。synchronizedメソッドAがスレッド1に呼び出されている間に、synchronizedメソッドBをスレッド2呼び出そうとした場合、呼び出しはスレッド1によるsynchronizedメソッドA呼び出しが終了するまで待たされる。
synchronizedメソッドを使用することで同期を取ることができるが、待ち時間が生じるため、動作に時間が掛かる可能性がある。
synchronizedメソッド以外にも、同期を取る機能としてsynchronizedブロックがある。
synchronizedメソッドは、synchronizedブロックをthisに対して行うものであり、自クラスのフィールドに対して排他制御を行いたい場合にはsynchronizedメソッドの方がいいだろう。
synchronizedで修飾されたメソッド。「synchronized 戻り値 メソッド名(引数){ 実装 }」のように、メソッドの宣言の先頭にsynchronizedが付けられたメソッド。
マルチスレッドにおいて、複数のスレッドが同時に呼び出すことができないメソッド。
synchronizedメソッドは、呼び出すスレッドを1つに限定することができる。2つのスレッドからほぼ同時に呼び出された場合、先に呼び出した側がメソッドを終了させない限り、後から呼び出した側が待たされる。
ただし、synchronizedメソッドは、そのメソッドが属するインスタンスが異なる場合には機能しない。つまりsynchronizedメソッドは、thisが同じ場合にのみ待たせることができ、thisが異なる場合には待たせない。
これは、synchronizedメソッドが「フィールドの同期を取る」ためのものだからである。複数のスレッドが同時にアクセスすると、先のスレッドがフィールドの値を書き換える前に後のスレッドがフィールドの値を読み取り、値が誤ったものになってしまう可能性があるからである。
synchronizedメソッドはそれを防ぐためのものである。フィールドにアクセスするメソッドをsynchronizedメソッドとすることで、先のスレッドがメソッドを呼び出し終えるまで後のスレッドを待たせることができる。それにより、フィールドの値を常に保つことができる。
インスタンスが異なれば、フィールドも異なるため、同期を取る必要はない。インスタンスが異なるとsynchronizedメソッドが機能しないのはこのためである。
また、この目的のため、synchronizedメソッドは全て同期の対象となる。
synchronizedメソッドが複数存在する場合、同じように機能する。synchronizedメソッドAがスレッド1に呼び出されている間に、synchronizedメソッドBをスレッド2呼び出そうとした場合、呼び出しはスレッド1によるsynchronizedメソッドA呼び出しが終了するまで待たされる。
synchronizedメソッドを使用することで同期を取ることができるが、待ち時間が生じるため、動作に時間が掛かる可能性がある。
synchronizedメソッド以外にも、同期を取る機能としてsynchronizedブロックがある。
synchronizedメソッドは、synchronizedブロックをthisに対して行うものであり、自クラスのフィールドに対して排他制御を行いたい場合にはsynchronizedメソッドの方がいいだろう。
参考サイト
// Sample.java
public class Sample
{
public static void main( String[] args )
{
try
{
SynchronizeClass synchronizeClass = new SynchronizeClass();
// 別スレッドの方を呼び出します。
OtherThread thread = new OtherThread( synchronizeClass );
thread.start();
// 2秒待ちます。OtherThreadの方を先に
// 実行するためです。
Thread.sleep( 2 * 1000 );
// こちらでも。
System.out.println( "Sample開始" );
// synchronizedメソッドを呼び出します。
synchronizeClass.synchronizedMethod( "Sample" );
System.out.println( "Sample終了" );
// OtherThread開始
// synchronizedMethod()メソッド開始 from OtherThread
// Sample開始
// synchronizedMethod()メソッド終了 from OtherThread
// synchronizedMethod()メソッド開始 from Sample
// OtherThread終了
// synchronizedMethod()メソッド終了 from Sample
// Sample終了
// このように、synchronizedメソッドは、一度に1つの
// スレッドしか呼び出せません。先に呼び出した方が
// 終了するまで待ちます。
// synchronizedメソッドの排他処理は、インスタンスに
// 対して行われます。そのため、同じsynchronizedメソッドでも、
// インスタンス(synchronizedメソッドにとってはthis)が
// 異なれば排他されません。
thread = new OtherThread( synchronizeClass );
thread.start();
// 2秒待ちます。OtherThreadの方を先に
// 実行するためです。
Thread.sleep( 2 * 1000 );
// こちらでも。
System.out.println( "Sample開始" );
// synchronizedメソッドを呼び出します。ただし、
// 別のインスタンスを作ってそちらのメソッドを
// 呼び出します。
SynchronizeClass synchronizeClass2 = new SynchronizeClass();
synchronizeClass2.synchronizedMethod( "Sample" );
System.out.println( "Sample終了" );
// OtherThread開始
// synchronizedMethod()メソッド開始 from OtherThread
// Sample開始
// synchronizedMethod()メソッド開始 from Sample
// synchronizedMethod()メソッド終了 from OtherThread
// OtherThread終了
// synchronizedMethod()メソッド終了 from Sample
// Sample終了
// このように、インスタンスが異なれば排他されません。
// インスタンスが同じであれば異なるsynchronizedメソッドでも
// 排他されます。
thread = new OtherThread( synchronizeClass );
thread.start();
// 2秒待ちます。OtherThreadの方を先に
// 実行するためです。
Thread.sleep( 2 * 1000 );
// こちらでも。
System.out.println( "Sample開始" );
// もうひとつのsynchronizedメソッドを呼び出します。
synchronizeClass.synchronizedMethod2( "Sample" );
System.out.println( "Sample終了" );
// OtherThread開始
// synchronizedMethod()メソッド開始 from OtherThread
// Sample開始
// synchronizedMethod()メソッド終了 from OtherThread
// synchronizedMethod2()メソッド開始 from Sample
// OtherThread終了
// synchronizedMethod2()メソッド終了 from Sample
// Sample終了
// このように、synchronizedMethod()メソッドが
// 終わるまで、synchronizedMethod2()メソッドが
// 呼べなくなっています。
}
catch( InterruptedException e )
{
// sleep()メソッドが途中で中断されると
// InterruptedException例外が投げられます。
// 滅多にないですが。
e.printStackTrace();
}
}
}
/**
* 別スレッドとして実行するためのクラス。
*/
class OtherThread extends Thread
{
/** SynchronizeClassクラス。 */
private SynchronizeClass synchronizeClass;
/** コンストラクタ。 */
public OtherThread( SynchronizeClass synchronizeClass )
{
this.synchronizeClass = synchronizeClass;
}
/**
* Threadクラスのrun()メソッドを
* オーバーライドしたメソッド。このメソッドが
* 別スレッドとして呼び出されます。
*/
public void run()
{
System.out.println( "OtherThread開始" );
// synchronizedメソッドを呼び出します。
// このインスタンスは、Sampleクラスのmain()メソッドで
// 作られたものです。
synchronizeClass.synchronizedMethod( "OtherThread" );
System.out.println( "OtherThread終了" );
}
}
/**
* 同期処理テスト用クラス。
*/
class SynchronizeClass
{
/** フィールド。 */
private int data = 0;
/**
* synchronizedメソッド。
*/
public synchronized void synchronizedMethod( String name )
{
try
{
System.out.println( "synchronizedMethod()メソッド開始 from " + name );
// 5秒待ちます。
Thread.sleep( 5 * 1000 );
System.out.println( "synchronizedMethod()メソッド終了 from " + name );
// たとえば、dataフィールドにアクセスする場合。
data = 100;
// synchronizedによる同期は、この「フィールドへのアクセス」を
// 1スレッドに限定するためのものです。だから、
// このメソッドにとってのインスタンス(つまりthis)が
// 異なる場合には同期が取られないのです。
}
catch( InterruptedException e )
{
// sleep()メソッドが途中で中断されると
// InterruptedException例外が投げられます。
// 滅多にないですが。
e.printStackTrace();
}
}
/**
* もうひとつのsynchronizedメソッド。
*/
public synchronized void synchronizedMethod2( String name )
{
try
{
System.out.println( "synchronizedMethod2()メソッド開始 from " + name );
// 5秒待ちます。
Thread.sleep( 5 * 1000 );
System.out.println( "synchronizedMethod2()メソッド終了 from " + name );
// たとえば、dataフィールドにアクセスする場合。
data = 100;
// synchronizedによる同期は、この「フィールドへのアクセス」を
// 1スレッドに限定するためのものです。だから、
// フィールドに対してアクセスする複数のメソッドを
// 同時に排他できるようになっているのです。
}
catch( InterruptedException e )
{
// sleep()メソッドが途中で中断されると
// InterruptedException例外が投げられます。
// 滅多にないですが。
e.printStackTrace();
}
}
}
public class Sample
{
public static void main( String[] args )
{
try
{
SynchronizeClass synchronizeClass = new SynchronizeClass();
// 別スレッドの方を呼び出します。
OtherThread thread = new OtherThread( synchronizeClass );
thread.start();
// 2秒待ちます。OtherThreadの方を先に
// 実行するためです。
Thread.sleep( 2 * 1000 );
// こちらでも。
System.out.println( "Sample開始" );
// synchronizedメソッドを呼び出します。
synchronizeClass.synchronizedMethod( "Sample" );
System.out.println( "Sample終了" );
// OtherThread開始
// synchronizedMethod()メソッド開始 from OtherThread
// Sample開始
// synchronizedMethod()メソッド終了 from OtherThread
// synchronizedMethod()メソッド開始 from Sample
// OtherThread終了
// synchronizedMethod()メソッド終了 from Sample
// Sample終了
// このように、synchronizedメソッドは、一度に1つの
// スレッドしか呼び出せません。先に呼び出した方が
// 終了するまで待ちます。
// synchronizedメソッドの排他処理は、インスタンスに
// 対して行われます。そのため、同じsynchronizedメソッドでも、
// インスタンス(synchronizedメソッドにとってはthis)が
// 異なれば排他されません。
thread = new OtherThread( synchronizeClass );
thread.start();
// 2秒待ちます。OtherThreadの方を先に
// 実行するためです。
Thread.sleep( 2 * 1000 );
// こちらでも。
System.out.println( "Sample開始" );
// synchronizedメソッドを呼び出します。ただし、
// 別のインスタンスを作ってそちらのメソッドを
// 呼び出します。
SynchronizeClass synchronizeClass2 = new SynchronizeClass();
synchronizeClass2.synchronizedMethod( "Sample" );
System.out.println( "Sample終了" );
// OtherThread開始
// synchronizedMethod()メソッド開始 from OtherThread
// Sample開始
// synchronizedMethod()メソッド開始 from Sample
// synchronizedMethod()メソッド終了 from OtherThread
// OtherThread終了
// synchronizedMethod()メソッド終了 from Sample
// Sample終了
// このように、インスタンスが異なれば排他されません。
// インスタンスが同じであれば異なるsynchronizedメソッドでも
// 排他されます。
thread = new OtherThread( synchronizeClass );
thread.start();
// 2秒待ちます。OtherThreadの方を先に
// 実行するためです。
Thread.sleep( 2 * 1000 );
// こちらでも。
System.out.println( "Sample開始" );
// もうひとつのsynchronizedメソッドを呼び出します。
synchronizeClass.synchronizedMethod2( "Sample" );
System.out.println( "Sample終了" );
// OtherThread開始
// synchronizedMethod()メソッド開始 from OtherThread
// Sample開始
// synchronizedMethod()メソッド終了 from OtherThread
// synchronizedMethod2()メソッド開始 from Sample
// OtherThread終了
// synchronizedMethod2()メソッド終了 from Sample
// Sample終了
// このように、synchronizedMethod()メソッドが
// 終わるまで、synchronizedMethod2()メソッドが
// 呼べなくなっています。
}
catch( InterruptedException e )
{
// sleep()メソッドが途中で中断されると
// InterruptedException例外が投げられます。
// 滅多にないですが。
e.printStackTrace();
}
}
}
/**
* 別スレッドとして実行するためのクラス。
*/
class OtherThread extends Thread
{
/** SynchronizeClassクラス。 */
private SynchronizeClass synchronizeClass;
/** コンストラクタ。 */
public OtherThread( SynchronizeClass synchronizeClass )
{
this.synchronizeClass = synchronizeClass;
}
/**
* Threadクラスのrun()メソッドを
* オーバーライドしたメソッド。このメソッドが
* 別スレッドとして呼び出されます。
*/
public void run()
{
System.out.println( "OtherThread開始" );
// synchronizedメソッドを呼び出します。
// このインスタンスは、Sampleクラスのmain()メソッドで
// 作られたものです。
synchronizeClass.synchronizedMethod( "OtherThread" );
System.out.println( "OtherThread終了" );
}
}
/**
* 同期処理テスト用クラス。
*/
class SynchronizeClass
{
/** フィールド。 */
private int data = 0;
/**
* synchronizedメソッド。
*/
public synchronized void synchronizedMethod( String name )
{
try
{
System.out.println( "synchronizedMethod()メソッド開始 from " + name );
// 5秒待ちます。
Thread.sleep( 5 * 1000 );
System.out.println( "synchronizedMethod()メソッド終了 from " + name );
// たとえば、dataフィールドにアクセスする場合。
data = 100;
// synchronizedによる同期は、この「フィールドへのアクセス」を
// 1スレッドに限定するためのものです。だから、
// このメソッドにとってのインスタンス(つまりthis)が
// 異なる場合には同期が取られないのです。
}
catch( InterruptedException e )
{
// sleep()メソッドが途中で中断されると
// InterruptedException例外が投げられます。
// 滅多にないですが。
e.printStackTrace();
}
}
/**
* もうひとつのsynchronizedメソッド。
*/
public synchronized void synchronizedMethod2( String name )
{
try
{
System.out.println( "synchronizedMethod2()メソッド開始 from " + name );
// 5秒待ちます。
Thread.sleep( 5 * 1000 );
System.out.println( "synchronizedMethod2()メソッド終了 from " + name );
// たとえば、dataフィールドにアクセスする場合。
data = 100;
// synchronizedによる同期は、この「フィールドへのアクセス」を
// 1スレッドに限定するためのものです。だから、
// フィールドに対してアクセスする複数のメソッドを
// 同時に排他できるようになっているのです。
}
catch( InterruptedException e )
{
// sleep()メソッドが途中で中断されると
// InterruptedException例外が投げられます。
// 滅多にないですが。
e.printStackTrace();
}
}
}
// Sample.java public class Sample { public static void main( String[] args ) { try { SynchronizeClass synchronizeClass = new SynchronizeClass(); // 別スレッドの方を呼び出します。 OtherThread thread = new OtherThread( synchronizeClass ); thread.start(); // 2秒待ちます。OtherThreadの方を先に // 実行するためです。 Thread.sleep( 2 * 1000 ); // こちらでも。 System.out.println( "Sample開始" ); // synchronizedメソッドを呼び出します。 synchronizeClass.synchronizedMethod( "Sample" ); System.out.println( "Sample終了" ); // OtherThread開始 // synchronizedMethod()メソッド開始 from OtherThread // Sample開始 // synchronizedMethod()メソッド終了 from OtherThread // synchronizedMethod()メソッド開始 from Sample // OtherThread終了 // synchronizedMethod()メソッド終了 from Sample // Sample終了 // このように、synchronizedメソッドは、一度に1つの // スレッドしか呼び出せません。先に呼び出した方が // 終了するまで待ちます。 // synchronizedメソッドの排他処理は、インスタンスに // 対して行われます。そのため、同じsynchronizedメソッドでも、 // インスタンス(synchronizedメソッドにとってはthis)が // 異なれば排他されません。 thread = new OtherThread( synchronizeClass ); thread.start(); // 2秒待ちます。OtherThreadの方を先に // 実行するためです。 Thread.sleep( 2 * 1000 ); // こちらでも。 System.out.println( "Sample開始" ); // synchronizedメソッドを呼び出します。ただし、 // 別のインスタンスを作ってそちらのメソッドを // 呼び出します。 SynchronizeClass synchronizeClass2 = new SynchronizeClass(); synchronizeClass2.synchronizedMethod( "Sample" ); System.out.println( "Sample終了" ); // OtherThread開始 // synchronizedMethod()メソッド開始 from OtherThread // Sample開始 // synchronizedMethod()メソッド開始 from Sample // synchronizedMethod()メソッド終了 from OtherThread // OtherThread終了 // synchronizedMethod()メソッド終了 from Sample // Sample終了 // このように、インスタンスが異なれば排他されません。 // インスタンスが同じであれば異なるsynchronizedメソッドでも // 排他されます。 thread = new OtherThread( synchronizeClass ); thread.start(); // 2秒待ちます。OtherThreadの方を先に // 実行するためです。 Thread.sleep( 2 * 1000 ); // こちらでも。 System.out.println( "Sample開始" ); // もうひとつのsynchronizedメソッドを呼び出します。 synchronizeClass.synchronizedMethod2( "Sample" ); System.out.println( "Sample終了" ); // OtherThread開始 // synchronizedMethod()メソッド開始 from OtherThread // Sample開始 // synchronizedMethod()メソッド終了 from OtherThread // synchronizedMethod2()メソッド開始 from Sample // OtherThread終了 // synchronizedMethod2()メソッド終了 from Sample // Sample終了 // このように、synchronizedMethod()メソッドが // 終わるまで、synchronizedMethod2()メソッドが // 呼べなくなっています。 } catch( InterruptedException e ) { // sleep()メソッドが途中で中断されると // InterruptedException例外が投げられます。 // 滅多にないですが。 e.printStackTrace(); } } } /** * 別スレッドとして実行するためのクラス。 */ class OtherThread extends Thread { /** SynchronizeClassクラス。 */ private SynchronizeClass synchronizeClass; /** コンストラクタ。 */ public OtherThread( SynchronizeClass synchronizeClass ) { this.synchronizeClass = synchronizeClass; } /** * Threadクラスのrun()メソッドを * オーバーライドしたメソッド。このメソッドが * 別スレッドとして呼び出されます。 */ public void run() { System.out.println( "OtherThread開始" ); // synchronizedメソッドを呼び出します。 // このインスタンスは、Sampleクラスのmain()メソッドで // 作られたものです。 synchronizeClass.synchronizedMethod( "OtherThread" ); System.out.println( "OtherThread終了" ); } } /** * 同期処理テスト用クラス。 */ class SynchronizeClass { /** フィールド。 */ private int data = 0; /** * synchronizedメソッド。 */ public synchronized void synchronizedMethod( String name ) { try { System.out.println( "synchronizedMethod()メソッド開始 from " + name ); // 5秒待ちます。 Thread.sleep( 5 * 1000 ); System.out.println( "synchronizedMethod()メソッド終了 from " + name ); // たとえば、dataフィールドにアクセスする場合。 data = 100; // synchronizedによる同期は、この「フィールドへのアクセス」を // 1スレッドに限定するためのものです。だから、 // このメソッドにとってのインスタンス(つまりthis)が // 異なる場合には同期が取られないのです。 } catch( InterruptedException e ) { // sleep()メソッドが途中で中断されると // InterruptedException例外が投げられます。 // 滅多にないですが。 e.printStackTrace(); } } /** * もうひとつのsynchronizedメソッド。 */ public synchronized void synchronizedMethod2( String name ) { try { System.out.println( "synchronizedMethod2()メソッド開始 from " + name ); // 5秒待ちます。 Thread.sleep( 5 * 1000 ); System.out.println( "synchronizedMethod2()メソッド終了 from " + name ); // たとえば、dataフィールドにアクセスする場合。 data = 100; // synchronizedによる同期は、この「フィールドへのアクセス」を // 1スレッドに限定するためのものです。だから、 // フィールドに対してアクセスする複数のメソッドを // 同時に排他できるようになっているのです。 } catch( InterruptedException e ) { // sleep()メソッドが途中で中断されると // InterruptedException例外が投げられます。 // 滅多にないですが。 e.printStackTrace(); } } }