在这个内部锁的预言机示例中,监视器对象从未被声明为易失性,最终或与任何其他常规对象没有任何区别
有很多public class MsLunch { private long c1 = 0; private long c2 = 0; private Object lock1 = new Object(); private Object lock2 = new Object(); public void inc1() { synchronized(lock1) { c1++; } } public void inc2() { synchronized(lock2) { c2++; } } }
问题在争论易失性块与同步块
- 易失场和同步块,
- Java 中易失性和同步性之间的区别
- 何时在 Java 的多线程中使用易失性与同步,
- 您是否曾经在 Java 中使用过 volatile 关键字
和不可变对象
- 什么是不可变对象,
- Java 中的不变性和同步 性
- 不可变对象 Java Concurreny)在多线程中。
作为旁注,我理解声明对象 final与不变性为什么可以修改最终对象以及为什么将锁定对象声明为 final 不会使其不可变之间的微妙区别。
但是,我们有著名的单例类延迟初始化模式,其中volatile
变量的使用是必不可少的。
public class SingletonDemo { private static volatile SingletonDemo instance; private SingletonDemo() { } public static SingletonDemo getInstance() { if (instance == null ) { synchronized (SingletonDemo.class) { if (instance == null) { instance = new SingletonDemo(); } } } return instance; } }
在上面的代码示例中,它使用 Class 对象作为锁。
既然对于由多个线程访问的对象,您需要使用上述某种机制来确保原子访问,为什么对于固有锁对象不需要任何特殊处理?
这些锁不需要特殊处理,因为MsLunch
对象本身需要先发布,然后才能被任何其他线程看到。
public class MyMain {
public static void main(String... args) {
MsLunch lunch = new MsLunch();
// ...
这是线程安全的,因为局部变量 (" lunch
") 对多个线程不可见。
接下来,下面的类使本地引用对系统中的所有线程可见。 发生这种情况时,我们需要使用 volatile
. volatile
关键字有效地创建了一个内存屏障,用于安全地发布对象。 这包括在赋值之前进行的所有写入,包括在构造对象时在内部进行的写入。
C.f. 安全出版
public class MyMain {
public static volatile MsLunch publicLunch;
public static void main(String... args) {
MsLunch lunch = new MsLunch();
publicLunch = lunch;
//...
}
}
它可能应该是最终的。 但是 final 并不是什么特别的东西——它只在一种特殊情况下需要(将函数内声明的变量引用到匿名类中)。 任何其他情况 final只是提醒程序员不要覆盖变量 - 您可以在程序中删除单词final的所有其他使用,它将完美运行。 你是对的,程序员可以分配给它,然后引起问题。 但如果他不这样做,那就没有问题了。 因此,在创建 final 时继续使用 final,但程序不需要编译。
至于静态 - 取决于用例。 是要监视类的所有实例,还是独立监视每个实例? 在第一种情况下,您在第二种情况下不使用静态。
不需要易失性,因为多个线程实际上并未更改对象。 它正在同步。 这是完全不同的,并且Java语言的旧部分比易失性更旧。 没有必要使变量易失性,因为您不会更改它,并且用于监视对象的内部数据结构已经知道它们需要线程安全(并且以比易失性承诺更强大的方式)。
在这个内部锁的预言机示例中,监视器对象从未声明为易失性、最终对象或与任何其他常规对象没有任何区别。
那不是真的。见下文。
既然对于由多个线程访问的对象,您需要使用上述某种机制来确保原子访问,为什么对于固有锁对象不需要任何特殊处理?
它确实有特殊待遇。它是同步的。