我无法理解,如果我的变量既是volatile变量又是static变量,那么为什么线程未在输出中反映公共共享值?最后几行的输出是:线程正在运行4998Thread-0线程正在运行4999Thread-0线程正在运行4899Thread -1线程正在运行
public class Test implements Runnable{
volatile static int i=0;
@Override
public void run() {
for(;i<5000;i++)
{ try {
Thread t = Thread.currentThread();
String name = t.getName();
// Thread.sleep(10);
System.out.println(i+name);
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("Thread is running");
}}
public static void main(String[] args) {
Test t=new Test();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
// t.run();
t2.start();
}
}
易失性确保变量的更改对其他线程可见。但是它不能确保同步。
从您的程序的一次执行中,我得到以下输出:
Thread is running
3474Thread-1
Thread is running
3475Thread-0
Thread is running
3477Thread-0
(.... many lines where i goes from 3478 to 4998, with Thread-0 always being the one running...)
Thread is running
4999Thread-0
Thread is running
3476Thread-1
Thread is running
发生这种情况是因为线程获得了要运行的处理器时间的分片,并且它们的执行可以随时暂停和恢复。这里的线程1正在执行“ System.out.println(i + name);”行。我的值为3476。i+ name的值评估为“ 3476Thread-1”,但此时Thread-1执行停止,而Thread-0获得其时间片。线程-0一直执行到完成。然后线程1再次执行。在i + name的值被评估为“ 3476Thread-1”之后以及在调用println之前,我们已将其保留。调用现在已完成并打印,因此您将在结束时看到“ 3476Thread-1”。线程0将我的线程数增加到5000,但这并没有改变对i + name的求值结果,该求值结果是在所有这些增长之前完成的。
问题是i ++和i + name是不同的指令,线程执行可以在它们之间暂停和恢复。为了确保获得保密输出,需要确保i ++和i + name之间没有中断。也就是说,您需要使该指令集原子化。
public class Test implements Runnable{
static Object lock = new Object();
volatile static int i=0;
@Override
public void run() {
for(;;)
{
try {
Thread t = Thread.currentThread();
String name = t.getName();
synchronized( lock )
{
if ( i>=5000 )
break;
i++;
System.out.println(i+name);
}
// Thread.sleep(10);
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("Thread is running");
}
} }
public static void main(String[] args) {
Test t=new Test();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
// t.run();
t2.start();
}
}
在该程序中,如果Thread-1在i ++和i + name之间暂停,则它将位于由synced(lock)控制的关键区域内。当线程-0开始执行时,它将到达synchronized(lock)指令,并且必须停止执行,直到线程-1恢复并退出该块为止。因为JLS确保:
同步语句(第14.19节)计算对对象的引用;然后,它尝试在该对象的监视器上执行锁定操作,并且在锁定操作成功完成之前不会进一步进行操作。执行锁定操作后,将执行synced语句的主体。如果主体的执行正常或突然完成,则会在同一监视器上自动执行解锁动作。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句