みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

synchronizedについて

こんにちは!質問です!

javaのsynchronizedについて教えてください。

DBへの更新メソッドに、
排他制御をしようと思っているのですが、調べて自分で
コーディングしました。アドバイスをお願いします!

public static synchronized void update() throws SQLException{
Connection db = null;
PreparedStatement objPs = null;
ResultSet rs = null;
StringBuffer sql = new StringBuffer();
try {
Context ctx=new InitialContext();
DataSource ds=(DataSource)ctx.lookup("java:/comp/env/test/sample");
db=ds.getConnection();
sql.append("UPDATE ");
sql.append("test ");
sql.append("SET ");
sql.append("aaa = '000', ");
sql.append("bbb = '000', ");
sql.append("ccc = '000' ");
sql.append("WHERE dd = '0'");
objPs=db.prepareStatement(sql.toString());
objPs.executeUpdate();
} catch (SQLException e) {
//エラー処理
} catch(Exception e) {
//エラー処理
} finally {
try {
if(rs!=null) {rs.close();}
if(objPs!=null){objPs.close();}
if(db!=null) {db.close();}
} catch(Exception e){
e.printStackTrace();
}
}
}

クラスはpublicですが、static synchronized にしていたら、
排他制御が可能でしょうか?
staticなので、インスタンス複数でも1つしか存在しないのですよね?
その場合、このメソッドを呼び出しているスレッドが終了しないうちに
別のスレッドが呼び出した場合、そのスレッドはどうなるのでしょうか?
目で見て確認する方法もできれば、教えていただきたいです!

また、sql発行メソッド(上記メソッド)を直接排他制御するのと、
上記メソッドの呼出元を排他制御するのと、どちらがいいとかって
あるのですか?

ご存知の方、よろしくお願い致しますm(_ _)m

投稿日時 - 2007-07-23 13:51:54

QNo.3192201

すぐに回答ほしいです

質問者が選んだベストアンサー

> また、static synchronized と synchronized
> どういう違いがあって、どちらを使ったらいいのでしょうか?

synchronizedはそれを含むオブジェクトを排他制御の対象にします。
staticメソッドのsynchonizedは同じクラスのstaticメソッドに対して、
staticでないメソッドのsynchonizedは同じインスタンス中の他のsynchronizedに対して
排他制御がかかります。
今回のような場合はインスタンス化せず、staticで排他制御を行ったほうが良いかと思います。

サンプルコードを書きましたのでご覧ください。
staticメソッドのupdateとインスタンスメソッドのget同士では排他制御がかかりません。
さらにメソッド中でフィールド変数を使用していると、これに対しても排他制御がかかりません。注意が必要です。

public class SynchroTest1 {

private static String str;

public static void main( String[] args ) {
for( int i = 0; i < 10; i++ ){
new SyncroTest1Thread( i ).start();
}
}

public static synchronized void update(String arg) {
System.out.println( "-- " + arg + " start.");
str = arg;
try {
Thread.sleep( 500 );
} catch ( InterruptedException e ) {
e.printStackTrace();
}
System.out.println( "-- " + arg + " finish." + str );
}

public synchronized void get( String arg ) {
System.out.println( "++ " + arg + " start.");
str = arg;
try {
Thread.sleep( 500 );
} catch ( InterruptedException e ) {
e.printStackTrace();
}
System.out.println( "++ " + arg + " finish." + str );
}
}

class SyncroTest1Thread extends Thread {
int id;

SyncroTest1Thread ( int i ) {
id = i;
}
public void run() {
System.out.println( "Thread " + id + "in");
if( id % 2 == 0)
SynchroTest1.update( "Thread " + id );
else
new SynchroTest1().get( "Thread " + id );

System.out.println( "Thread " + id + "out");
}
}

投稿日時 - 2007-07-23 17:36:31

お礼

こんばんは。ご回答ありがとうございます!

とても丁寧に説明して頂いて本当にありがとうございました☆
説明通りでした!

これから、サーブレットでも試してみます!

本当にありがとうございました☆

投稿日時 - 2007-07-23 20:31:44

ANo.3

このQ&Aは役に立ちましたか?

1人が「このQ&Aが役に立った」と投票しています

回答(3)

ANo.2

分割して回答します。

> アプリケーション全体で、1ユーザがupdateしている間
> メソッドにロックをかけたいのですが、可能ですか?
>
> bgbgさんから頂いたサンプルをコマンドプロンプトを2つ立ち上げて、立て続けに実行したところ、
> ほぼ同時に終了したのですが、staticだからといっても、
> この場合、別々にstatic領域があると考えるのですか?

コマンドプロンプト2つで実行したということは、それぞれ別のVMで起動しているということです。
VM自体が別なのでVM同士での排他制御は行われません。
通常ServletなどのWebアプリケーションは複数のリクエストでも1つのVMで動作するので、実際サーブレットを立ち上げて別々に接続してみると、きちんと排他制御されます。
サーブレットに同じ処理を書いて実験してみてください。

投稿日時 - 2007-07-23 17:25:43

ANo.1

> その場合、このメソッドを呼び出しているスレッドが終了しないうちに
> 別のスレッドが呼び出した場合、そのスレッドはどうなるのでしょうか?

後から呼び出されたスレッドが待機状態になります。
前のスレッドのsynchronizedブロックが終了したら、後のスレッドが実行可能になります。

> また、sql発行メソッド(上記メソッド)を直接排他制御するのと、
> 上記メソッドの呼出元を排他制御するのと、どちらがいいとかって
> あるのですか?

synchronizedブロックは、そのブロックと同じオブジェクトに属する全てのsynchronizedブロックに対し排他制御をかけます。
上記メソッドの呼び出し元を排他制御しても、上記メソッド自体は排他制御されません。
つまり別のオブジェクトやメソッドから上記メソッドに自由にアクセス可能です。
SQLでの更新を制御するのが目的なら、上記メソッドに排他制御をかけるのが正しいです。

参考URLにより詳しい解説があります。ぜひご覧ください。

> 目で見て確認する方法もできれば、教えていただきたいです!

簡単なサンプルコードを。
updateとgetはともにsynchronizedブロックで、どちらかのメソッドに処理が入ると終了するまでどちらのメソッドにも処理が入れません。

public class SynchroTest1 {

public static void main( String[] args ) {
for( int i = 0; i < 10; i++ ){
new SyncroTest1Thread( i ).start();
}
}

public static synchronized void update(String arg) {
System.out.println( "-- " + arg + " start.");
try {
Thread.sleep( 500 );
} catch ( InterruptedException e ) {
e.printStackTrace();
}
System.out.println( "-- " + arg + " finish.");
}

public static synchronized void get( String arg ) {
System.out.println( "++ " + arg + " start.");
try {
Thread.sleep( 500 );
} catch ( InterruptedException e ) {
e.printStackTrace();
}
System.out.println( "++ " + arg + " finish.");
}
}

class SyncroTest1Thread extends Thread {
int id;

SyncroTest1Thread ( int i ) {
id = i;
}
public void run() {
System.out.println( "Thread " + id + "in");
if( id % 2 == 0)
SynchroTest1.update( "Thread " + id );
else
SynchroTest1.get( "Thread " + id );

System.out.println( "Thread " + id + "out");
}
}

参考URL:http://www.javaroad.jp/java_thread4.htm

投稿日時 - 2007-07-23 15:30:55

補足

こんにちは!ご回答ありがとうございました☆

サンプルソースで確認したところ、
synchronized の排他制御が理解できました。
ありがとうございました☆

まだ、初歩的なことで、疑問に残る点なのですが、
webアプリケーションでこの様な排他制御をしようと
思っているので、
アプリケーション全体で、1ユーザがupdateしている間
メソッドにロックをかけたいのですが、可能ですか?

bgbgさんから頂いたサンプルをコマンドプロンプトを2つ立ち上げて、立て続けに実行したところ、
ほぼ同時に終了したのですが、staticだからといっても、
この場合、別々にstatic領域があると考えるのですか?

また、static synchronized と synchronized
どういう違いがあって、どちらを使ったらいいのでしょうか?
wait()を使って、明示的に待機状態にした方がいいのですか?

色々と質問しておりますが、よろしくお願い致しますm(_ _)m

投稿日時 - 2007-07-23 16:18:30

あなたにオススメの質問