Java 线程同步方法
Java Thread Synchronized methods
我有以下代码
import java.util.concurrent.*;
public class dsd {
private static boolean stopRequested;
private static void requestStop() {
stopRequested = true;
}
private static synchronized boolean stopRequested() {
return stopRequested;
}
public static void main(String[] args)
throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
int i = 0;
while (!stopRequested())
i++;
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
requestStop();
}
}
问题是为什么即使 requestStop()
不同步也能正常工作?如果我尝试对 stopRequested()
做同样的事情,它就不再起作用了。为什么那个变量上的线程并发没有问题?我知道同步使变量以一致的状态出现在其他线程中。但是这里的变量没有同步,好像没有效果。
synchronized
是可重入的,获得同步块锁的同一个线程保证如果它仍然拥有锁,它将在下一次尝试获取时保持它,但这没有由于 stopRequested
的更改是在同步块之外进行的并且代码仍然有效,您的代码在实际情况下仍然可能存在竞争条件。
假设在执行 N
时,线程必须因 stopRequested()
满足条件而终止,同时调用 requestStop()
,不能保证线程终止在 N+1
因为变量 stopRequested
在两个访问器方法中都不受排他锁保护,变量也没有 volatile
修饰符。这意味着由于竞争条件,您对代码的正确性部分是错误的。
Java 语言规范没有说明该程序是否或何时终止。
如果 方法 requestStop()
和 stopRequested()
同步,您只能确定它会在大约一秒后终止。
如果其中一个不同步,或者两者都不同步,则程序可能会在一秒后终止,或五秒后终止,或者永远不会。它在不同的 Java 运行 时间环境中可能表现不同。它在不同的硬件上可能表现不同。它在一周中的不同日子可能会有不同的表现。
如果两种方法都没有 synchronized
,它的行为是未定义的。
在 Java 并发实践:
的前言中引用了 Dion Almaer 的一句话
Dion Almaer, former editor of TheServerSide, recently blogged (after a painful debugging session that ultimately revealed a threading bug) that most Java programs are so rife with concurrency bugs that they work only "by accident".
这是其中之一。 Java 语言规范不保证更新后的标志可见。如果您不遵守规则,那么关于您的线程是否看到您的标志的新值,您将受制于 JVM 实现。
有些 JVM 实现比其他实现更宽容,有些在设计上更积极地缓存内容。如果它在本地适合您,并不意味着它会在生产环境中工作。
synchronized
块在 Java 中有额外的语义。它们不仅提供代码块的独占执行,而且发出所谓的内存屏障,保证线程之间所有操作结果的可见性。
因此,当您调用 synchronized stopRequested()
方法时,您还使所有更改对当前线程可见。如果您从此方法中删除 synchronized
,您也会删除屏障,并且 JVM 不保证您在主线程中设置的标志在后台线程中可见。这就是为什么没有 synchronized
.
会出现问题的原因
我有以下代码
import java.util.concurrent.*;
public class dsd {
private static boolean stopRequested;
private static void requestStop() {
stopRequested = true;
}
private static synchronized boolean stopRequested() {
return stopRequested;
}
public static void main(String[] args)
throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
int i = 0;
while (!stopRequested())
i++;
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
requestStop();
}
}
问题是为什么即使 requestStop()
不同步也能正常工作?如果我尝试对 stopRequested()
做同样的事情,它就不再起作用了。为什么那个变量上的线程并发没有问题?我知道同步使变量以一致的状态出现在其他线程中。但是这里的变量没有同步,好像没有效果。
synchronized
是可重入的,获得同步块锁的同一个线程保证如果它仍然拥有锁,它将在下一次尝试获取时保持它,但这没有由于 stopRequested
的更改是在同步块之外进行的并且代码仍然有效,您的代码在实际情况下仍然可能存在竞争条件。
假设在执行 N
时,线程必须因 stopRequested()
满足条件而终止,同时调用 requestStop()
,不能保证线程终止在 N+1
因为变量 stopRequested
在两个访问器方法中都不受排他锁保护,变量也没有 volatile
修饰符。这意味着由于竞争条件,您对代码的正确性部分是错误的。
Java 语言规范没有说明该程序是否或何时终止。
如果 方法 requestStop()
和 stopRequested()
同步,您只能确定它会在大约一秒后终止。
如果其中一个不同步,或者两者都不同步,则程序可能会在一秒后终止,或五秒后终止,或者永远不会。它在不同的 Java 运行 时间环境中可能表现不同。它在不同的硬件上可能表现不同。它在一周中的不同日子可能会有不同的表现。
如果两种方法都没有 synchronized
,它的行为是未定义的。
在 Java 并发实践:
的前言中引用了 Dion Almaer 的一句话Dion Almaer, former editor of TheServerSide, recently blogged (after a painful debugging session that ultimately revealed a threading bug) that most Java programs are so rife with concurrency bugs that they work only "by accident".
这是其中之一。 Java 语言规范不保证更新后的标志可见。如果您不遵守规则,那么关于您的线程是否看到您的标志的新值,您将受制于 JVM 实现。
有些 JVM 实现比其他实现更宽容,有些在设计上更积极地缓存内容。如果它在本地适合您,并不意味着它会在生产环境中工作。
synchronized
块在 Java 中有额外的语义。它们不仅提供代码块的独占执行,而且发出所谓的内存屏障,保证线程之间所有操作结果的可见性。
因此,当您调用 synchronized stopRequested()
方法时,您还使所有更改对当前线程可见。如果您从此方法中删除 synchronized
,您也会删除屏障,并且 JVM 不保证您在主线程中设置的标志在后台线程中可见。这就是为什么没有 synchronized
.