懒汉式加载:
public class Singleton {
private volatile static Singleton singleton;
private Singleton(){
System.out.println("Singleton instance");
}
public static Singleton getSingleton(){
synchronized(Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
}
-
饿汉式加载
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return singleton;
}
}
单例模式的几个要素:
- 私有的构造方法
- 指向自己实例的静态私有引用
- 以自己实例为返回值的静态公开方法
单例模式的注意事项
- 只能通过单例是共有方法访问对象,不能通过放射,否则将会创建出一个新的对象
- 不要做断开单例对象的操作
- 多线程使用对象是应该保证线程的安全,例子中的懒汉式加载已经考虑的线程安全的情况
关键字volatile描述
这里需要引入多线程环境下考虑,volatile字面描述为可见的,多线程在访问对象时分为工作线程的数据及公共线程数据,volition的修饰保证了工作线程对属性的更新立刻会反应到公共线程下,从而保证的数据的完整性。
并发问题产生原因
首先,jvm本身有一块主内存,而多线程的时候,每个线程有自己的内存区域。当线程需要读写主内存的变量时,流程如下
- 线程将主内存的变量拷贝一份到线程内存
- 线程内存执行操作
- 线程内存将变量同步到主内存
那么没有任何保护措施的情况下,读取时和修改后两个节点都有可能出现当前线程内存与主内存之间的一致性问题。最终造成了并发问题
引入volatile之后 volatile关键字修饰变量之后,一旦内存中拷贝的变量发生变化,会立即造成其他线程中对此变量拷贝的失效,其他线程中读取此变量的时候就会到主内存中去重新拷贝。因此volatile能够保证共享变量的可见性,但并不能保证其原子性。换句话说能够保证对于变量的读取永远是最新的,但不能保证非原子操作的一致性。
在逻辑上我们可以这么理解,如果用synchronized来处理,那么执行count++时,count在主内存中的值,由读到写,全都只能被一个线程访问。是可以保证一致性的。用volatile修饰时,只是相当于当一个线程改变count值的时候,发出了消息让其他线程更新本地的count值,而其他线程中已经执行过的count赋值语句则不受任何影响,因此仍然会出现并发问题。
资料来自:w3c 、 百度资料等