假设:
public class TestSeven extends Thread {
private static int x;
public synchronized void doThings() {
int current = x;
current++;
x = current;
}
public void run() {
doThings();
}
}
哪种说法属实?
一个。编译失败。
B中。在运行时抛出异常。
℃。同步run()方法将使类成为线程安全的。
d。变量“x”中的数据可以防止并发访问问题。
电子。将doThings()方法声明为static将使类成为线程安全的。
F。将doThings()中的语句包装在synchronized(new Object()){}块中将使该类成为线程安全的。
是否足以将doThings()标记为已同步以使该类具有线程安全性?我看到正确答案是D但这个问题的模型答案是E,但我不明白为什么?
答案 0 :(得分:19)
电子。将doThings()方法声明为static将使类成为线程安全的。
这是一个棘手的答案。该方法已经同步,但在实例上,而状态是在静态字段中,即在类上。使它static synchronized
确实是正确的答案,因为它在类上同步,而不是在(无意义的)实例上同步。
d。变量“x”中的数据可以防止并发访问问题。
private static int x;
这是一个静态变量。它由类的所有实例共享,因此在单个实例上进行同步没有帮助,就像F没有帮助一样,它会在完全丢弃的虚拟对象上进行同步。
答案 1 :(得分:12)
同步方法在执行之前获取监视器(第17.1节)。
对于类(静态)方法,与类关联的监视器 使用方法类的对象。
对于实例方法,与此关联的监视器(对象 (使用该方法)。
这意味着在您提供的代码中,synchronized
关键字会导致方法在执行方法体之前获取this
上的锁定。但是,由于x
为static
,因此无法确保x
的更新是原子的。 (该类的另一个实例可以进入同步区域并同时进行更新,因为它们具有不同的this
值,因此具有不同的锁定。)
但是,声明doStuff
static会使对方法的所有调用都获得相同的锁(Class
上的锁),从而确保方法体中的互斥。
规范的确如下:
class A {
static synchronized void doSomething() {
// ...
}
}
与
完全相同class A {
static void doSomething() {
synchronized(A.class) {
// ...
}
}
}
类似地:
class B {
synchronized void doSomething() {
// ...
}
}
与
完全相同class B {
void doSomething() {
synchronized (this) {
// ...
}
}
}
答案 2 :(得分:6)
通过同步doThings()方法,您将保持特定TestSeven对象的锁定。但是,类的静态变量不属于对象本身的特定实例。它们属于Class对象TestSeven.class
。所以,要么你可以去找
synchronized (TestSeven.class){
int current = x;
current++;
x = current;
}
在你的doThings()方法中,它正在一个实例锁中获取类锁,这是一个过度的东西。因此,您可以将方法标记为静态,以便最终单独获取Class对象的锁定。
答案 3 :(得分:1)
由于x
是static
,其他线程可以在doThings
方法运行的同时修改它。制作doThings
static
会停止此操作。
答案 4 :(得分:-6)
我同意你的正确答案是D. 我会说E是不正确的,因为如果我将doThings()设置为静态并删除synchronized关键字,我可以启动50个TestSeven线程,它可能导致不正确的x值。
注意: 我在这里错了,我错过了没有静态的同步方法实际上使用实例作为锁定监视器而不是类本身的观点。