关于JMM与DCL的知识点
JMM中的关系总结:
工作内存对应多核处理器中的每个处理器的高速缓存。主存对应的是内存。
工作内存与主存的关系很象是分布式缓存与客户端的关系。堆的共享,是Java中线程通讯的基础。
工作内存的变量是主存中变量的copy。
工作内存的中的变量只有提交到主存才能被其它工作内存看到。
volatile:
volatile原理:不使用工作内存,而直接访问主存中的变量。适用的场景是经常变化的变量。这样就会保证每次读到的都是最新的内容。当然是牺牲了性能。
volatile还有一个作用就是禁止JVM对代码优化(reorder)
final与volatile的区别就是final是在构造方法执行完之后就不再改变。这也就不提供多线程修改的机会。当然是线程安全。但是,对于volatile,是提供多线程修改的机会的。只不过,直接操作主存。
所以,一定要将final与线程联系起来看。而不要象满大街文章说如何提高性能。
synchronized原理.
修饰块:
synchronized (obj){
......
}
obj可以是this,对象的一个属性。
obj又被称为monitor.
注意synchronized修改块方法的原理是一样的。只是修饰方法时,默认是以this为monitor.
通常总是把monitor认为是lock。原理是synchronized获得monitor对象之后,会在monitor上加锁。monitor并不是锁。通常很容易在程序中定义obj的名字为**Lock.这是不恰当的。但是不影响使用。
当针对monitor上锁之后,其它线程访问方法或代码块必须等上次操作给monitor解锁才行,解锁之后,其它线程才可以给monitor加锁并进一步访问。
synchronzied修改静态方法时,被锁定对象是Object.class,而不再是this了。
DCL的产生及解决:
首先DCL产生的原因是单例中使用了延迟加载.
即使针对对象使用volatile修饰,但是JVM仍然会对volatile与非volatile的属性reorder。估计除非全部是volatile属性才会避免reorder的问题。只要存在reorder,就有可能导致:“允许对象在构造函数未调用完前, 将共享变量的引用指向部分构造的对象, 虽然对象未完全实例化, 但已经不为null了”.
所以如果只要单例不要延迟加载的话可以这样实现:
class Singleton{
private Vector v;
private boolean inUse;
//不做延迟加载来避免DCL。
private static Singleton instance = new Singleton();
private Singleton()
{
v = new Vector();
inUse = true;
//...
}
public static Singleton getInstance()
{
return instance;
}
}
又要单例,又要延迟加载可以这样实现:内部类可以在引用的时候才加载。
public class Foo {
// 静态内部类, 只有当有引用时, 该类才会被装载
private static class LazyFoo {
public static Foo foo = new Foo();
}
public static Foo getInstance() {
return LazyFoo.foo;
}
}
如果又想单例,又延迟,又想多线程访问:
public class Foo {
private static class LazyFoo {
public static Foo foo = new Foo();
}
public static synchronized Foo getInstance() {
return LazyFoo.foo;
}
}