[*0x 004 C]Javaマルチスレッド:synchronized
まず、同期方法も本質的には同期制御ブロックであり(針がロックに対してthisの場合のみ、同期制御ブロックがロックされているのがthisでないと、同期方法に直接書き換えることができない)、同期方法の粒度を全方法と区別し、同期制御ブロックの粒度を方法の一部とすることができる。
この例から、Class錠(static方式に対して)とObject錠(非static方式に対して)は2つのロックであることがわかる。実験の結果から、この2つの鍵は衝突しないものであり、すなわち、Counter c=new Counter()があると仮定して、Thread Aがc.bump()を呼び出すと、synchronizedはcの対象だけロックされています。Thread BはCounter.lassBump(またはc.lassBump()を遠慮なく呼び出すことができます。Thread Aのリリースを待つ必要はありません。例えば:
ただし、Thread Aがc.bump()を呼び出したら、cをロックしたら、Thread Bはc.bump()を呼び出すことができなくなり、cの他の同期方法や同期制御ブロックThread Bもアクセスできなくなり、Thread Aがcのロック(つまり、c.bump()の同期部分が完了するまで待つだけです。Counterの非同期方法はロックの制限を受けず、Thread Aがcをロックしても、Thread Bは任意にcの非同期方法にアクセスすることができます。
p.s.ここでは、オブジェクトをロックするということは、属性がアクセスできないということではなく、ロックオブジェクトは、同期方法または同期制御ブロックだけをロックし、同期方法または同期制御ブロックの実行を他のスレッドに中断させないということである。あるフィールドを変更する同期方法がある場合、このフィールドは他の非同期方法でも変更できます。それでもデータが一致しない場合があります。
p.s.synchronizedキーワードはクラスの前に置いてもいいです。クラス内のすべての方法は同期方法です。
//
public class Counter {
int count;
static int classCount;
public synchronized void bump() {
System.out.println("bump() starts");
count++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("bump() ends");
}
public static synchronized void classBump() {
classCount++;
System.out.println("classBump()");
}
}
//
public class Counter {
int count;
static int classCount;
public void bump() {
synchronized (this) {
// ...
}
}
public static void classBump() {
synchronized (Counter.class) {
// ...
}
// Counter.class, Class.forName()
/**
try {
synchronized (Class.forName("Counter")) {
// ...
}
} catch (ClassNotFoundException e) {
e.printStack();
}
*/
}
}
この例から、Class錠(static方式に対して)とObject錠(非static方式に対して)は2つのロックであることがわかる。実験の結果から、この2つの鍵は衝突しないものであり、すなわち、Counter c=new Counter()があると仮定して、Thread Aがc.bump()を呼び出すと、synchronizedはcの対象だけロックされています。Thread BはCounter.lassBump(またはc.lassBump()を遠慮なく呼び出すことができます。Thread Aのリリースを待つ必要はありません。例えば:
public class MultiThreadTest8 {
public static void main(String[] args) {
final Counter c = new Counter();
Thread t1 = new Thread(
new Runnable() {
@Override public void run() {
c.bump();
}
}, "Runner 1");
Thread t2 = new Thread(
new Runnable() {
@Override public void run() {
//Counter.classBump();
c.classBump();
}
}, "Runner 2");
t1.setPriority(Thread.NORM_PRIORITY + 2);
t1.start();
t2.start();
// output:
/**
bump() starts
classBump()
bump() ends
*/
}
}
ただし、Thread Aがc.bump()を呼び出したら、cをロックしたら、Thread Bはc.bump()を呼び出すことができなくなり、cの他の同期方法や同期制御ブロックThread Bもアクセスできなくなり、Thread Aがcのロック(つまり、c.bump()の同期部分が完了するまで待つだけです。Counterの非同期方法はロックの制限を受けず、Thread Aがcをロックしても、Thread Bは任意にcの非同期方法にアクセスすることができます。
p.s.ここでは、オブジェクトをロックするということは、属性がアクセスできないということではなく、ロックオブジェクトは、同期方法または同期制御ブロックだけをロックし、同期方法または同期制御ブロックの実行を他のスレッドに中断させないということである。あるフィールドを変更する同期方法がある場合、このフィールドは他の非同期方法でも変更できます。それでもデータが一致しない場合があります。
p.s.synchronizedキーワードはクラスの前に置いてもいいです。クラス内のすべての方法は同期方法です。