考虑以下代码片段:
如果在主线程中,我在方法-
volatile CountDownLatch latch = new CountDownLatch(3);
new Thread(new ProcessThread("Worker1",latch, 20000)).start();//20 secs
new Thread(new ProcessThread("Worker2",latch, 60000)).start();//60 secs
new Thread(new ProcessThread("Worker3",latch, 40000)).start();//40 secs
我看到volatile
显示为非法修饰符。只有final
是允许的。And final保证初始化安全
public static class ProcessThread implements Runnable {
final CountDownLatch latch;
final long workDuration;
final String name;
public ProcessThread(String name, CountDownLatch latch, long duration){
this.name= name;
this.latch = latch;
this.workDuration = duration;
}
}
下面的对象即new CountDownLatch(3)
是正确构造的,但我也想确保上面对象分配给的引用latch
对它下面的代码是可见的。
final CountDownLatch latch = new CountDownLatch(3);
上面的代码是否保证初始化,使latch
对下面的代码完全可见,即
new Thread(new ProcessThread("Worker1",latch, 20000)).start();
局部变量驻留在堆栈上;当然,当你两次调用相同的方法时,它们在各自的堆栈上都有所有的局部变量。
volatile仅在多个线程写入相同的内存位置(在堆上)时才有意义。
对于方法体中的局部变量来说是完全没有意义的!
And final保证初始化安全。
局部变量不行:它只会阻止你重新赋值该变量。
final CountDownLatch latch = new CountDownLatch(3);
上面的代码是否保证初始化,使得latch对下面的代码(即
)完全可见?
。这段代码保证了这一点:
public static class ProcessThread implements Runnable {
final CountDownLatch latch;
// Plus the assignment in the constructor.
}
final
字段保证在构造函数完成后(通常)是可见的。源自JLS第17.5节:
对象在其构造函数完成时被认为已完全初始化。如果一个线程只能在对象完全初始化之后才能看到对该对象的引用,则可以保证看到该对象的
final
字段的正确初始化值。
本地执行的操作不会有其他线程的可见性或干扰问题,因此声明本地变量volatile没有意义。