Java 6线程输出不是异步的?
这段代码应该产生均匀和不均匀的输出,因为在任何方法上都没有同步。然而,JVM上的输出总是均匀的。我真的很困惑,因为这个例子直接来自Doug LeaJava 6线程输出不是异步的?,java,multithreading,asynchronous,Java,Multithreading,Asynchronous,这段代码应该产生均匀和不均匀的输出,因为在任何方法上都没有同步。然而,JVM上的输出总是均匀的。我真的很困惑,因为这个例子直接来自Doug Lea public class TestMethod implements Runnable { private int index = 0; public void testThisMethod() { index++; index++; System.out.println(Thread.cu
public class TestMethod implements Runnable {
private int index = 0;
public void testThisMethod() {
index++;
index++;
System.out.println(Thread.currentThread().toString() + " "
+ index );
}
public void run() {
while(true) {
this.testThisMethod();
}
}
public static void main(String args[]) {
int i = 0;
TestMethod method = new TestMethod();
while(i < 20) {
new Thread(method).start();
i++;
}
}
}
public类TestMethod实现可运行{
私有整数指数=0;
公共void testThisMethod(){
索引++;
索引++;
System.out.println(Thread.currentThread().toString()+“”
+指数);
}
公开募捐{
while(true){
this.testThisMethod();
}
}
公共静态void main(字符串参数[]){
int i=0;
TestMethod=新的TestMethod();
而(i<20){
新线程(方法).start();
i++;
}
}
}
输出
螺纹[螺纹-8,5,主螺纹]135134
螺纹[螺纹-8,5,主螺纹]135136
螺纹[螺纹-8,5,主螺纹]135138
螺纹[螺纹-8,5,主螺纹]135140
螺纹[螺纹-8,5,主螺纹]135142
线程[Thread-8,5,main]135144您没有将
索引标记为volatile。这意味着编译器可以优化对它的访问,它可能会将您的2个增量合并为一个加法。您没有将索引标记为volatile。这意味着编译器可以优化对它的访问,并且它可能会将您的2个增量合并到一个加法中。您不知道这一点。自动调度的要点是它不能保证。它可能会对运行相同代码的两个线程进行完全不同的处理。或者完全一样。或者完全一样一个小时然后突然变了
问题的关键是,即使你解决了其他答案中提到的问题,你仍然不能依靠事情以特定的方式出现;您必须始终准备好Java内存和线程模型允许的任何可能的交叉,这包括println
总是在偶数个增量之后发生,即使表面上看起来不太可能发生这种情况。您不知道这一点。自动调度的要点是它不能保证。它可能会对运行相同代码的两个线程进行完全不同的处理。或者完全一样。或者完全一样一个小时然后突然变了
问题的关键是,即使你解决了其他答案中提到的问题,你仍然不能依靠事情以特定的方式出现;您必须随时准备好Java内存和线程模型允许的任何可能的交叉,这包括println
总是在偶数个增量之后发生,即使表面上看起来不太可能发生这种情况。结果正如我所期望的。输出之间的索引将增加两次,并且线程之间没有交互
为了扭转这个问题,你为什么会期望奇数输出
编辑:哎呀。我错误地假设每个线程都创建了一个新的runnable,因此每个线程都有一个不同的索引,而不是共享索引。令人不安的是,这样一个有缺陷的答案竟然获得了3张赞成票……结果正是我所期望的。输出之间的索引将增加两次,并且线程之间没有交互
为了扭转这个问题,你为什么会期望奇数输出
编辑:哎呀。我错误地假设每个线程都创建了一个新的runnable,因此每个线程都有一个不同的索引,而不是共享索引。令人不安的是,这样一个有缺陷的答案是如何获得3票赞成票的…首先:正如其他人所指出的,根本不能保证线程在两个增量操作之间会被中断
请注意,打印到System.out
很可能会强制线程进行某种同步,因此当线程从中返回时,它们很可能刚刚开始一个时间片,因此它们可能会完成两个递增操作,然后等待System.out
的共享资源
尝试将System.out.println()替换为如下内容:
int snapshot = index;
if (snapshot % 2 != 0) {
System.out.println("Oh noes! " + snapshot);
}
首先:正如其他人所指出的,根本不能保证线程在两个增量操作之间会被中断
请注意,打印到System.out
很可能会强制线程进行某种同步,因此当线程从中返回时,它们很可能刚刚开始一个时间片,因此它们可能会完成两个递增操作,然后等待System.out
的共享资源
尝试将System.out.println()替换为如下内容:
int snapshot = index;
if (snapshot % 2 != 0) {
System.out.println("Oh noes! " + snapshot);
}
我尝试了volatile
,得到了以下结果(使用if
仅在奇数时打印):
回复评论:
索引实际上是共享的,因为我们有一个TestMethod
实例,但有许多Thread
s在我们现有的TestMethod
实例上调用testThisMethod()
代码(除上述内容外无任何更改):
public class TestMethod implements Runnable {
volatile private int index = 0;
public void testThisMethod() {
index++;
index++;
if(index % 2 != 0){
System.out.println(Thread.currentThread().toString() + " "
+ index );
}
}
public void run() {
while(true) {
this.testThisMethod();
}
}
public static void main(String args[]) {
int i = 0;
TestMethod method = new TestMethod();
while(i < 20) {
new Thread(method).start();
i++;
}
}
}
public类TestMethod实现可运行{
易失性私有int指数=0;
公共void testThisMethod(){
索引++;
索引++;
如果(索引%2!=0){
System.out.println(Thread.currentThread().toString()+“”
+指数);
}
}
公开募捐{
while(true){
this.testThisMethod();
}
}
公共静态void main(字符串参数[]){
int i=0;
TestMethod=新的TestMethod();
而(i<20){
新线程(方法).start();
i++;
}
}
}
我尝试了volatile
,得到了以下结果(w