单例模式
这是一种常见的“设计模式”,当在某些情况下,不需要多个实例,就可以使用单例模式来解决,如果想要尝试创建多个实例,代码就会报错。
可以有两种方式:
饿汉模式:当类加载的时候就会立刻实例化。
懒汉模式:类加载时不会实例化,当第一次调用这个类的时候才会去实例化。(可以仔细拼一下为啥叫这两个名字,一个急得想马上创建,一个等啊等啊不到万不得已需要使用了才实例化)
饿汉模式
类加载的时候就会立刻实例化
static class Singleton{
//这里的构造方法使用private,在类外就无法new这个实例了
private Singleton() {
}
//这里使用static,表示和实例无关,只与类有关
private static Singleton instance = new Singleton();
//gerInstance是获取实例的唯一方法
public static Singleton gerInstance() {
return instance;
}
}
public static void main(String[] args) {
Singleton s1 = Singleton.gerInstance();
Singleton s2 = Singleton.gerInstance();
System.out.println(s1 == s2);
}
在main方法中,看起来是创建了两个实例,但是我们的打印结果是true,说明这两个实例其实是一个。
懒汉模式
类加载的时候不会实例,只有在第一次调用这个类的时候才会去实例化
static class Singleton {
private Singleton() {
}
private static Singleton instance = null;
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
可以观察一下这个代码,当多线程同时调用getInstance方法时,是需要分成几步的。读取instance中的内容,判断是否为null,如果为null,就new实例。返回实例的地址。根据线程安全的思想,可以看出这个代码会导致线程不安全。
1、给getInstance加锁,保证原子性
static class Singleton {
private Singleton() {
}
private static Singleton instance = null;
public static Singleton getInstance() {
//也可以把锁加在这一行,只不过是锁粒度大一点
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
return instance;
}
}
仔细分析,可以发现,如果把实例化已经创建好之后,再去调用getInstance就属于线程安全了。只有在实例化之前是线程不安全的。就可以只在实例化之前加锁,实例化之后就可以不用加锁了,这样就降低了锁的粒度
2、再次进行判断,降低锁粒度
static class Singleton {
private Singleton() {
}
private static Singleton instance = null;
public static Singleton getInstance() {
//也可以把锁加在这一行,只不过是锁粒度大一点
if (instance == null) {
//这个判断是为了只在实例化之前调用加锁,降低锁的粒度
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
这个代码还存在一点线程不安全的问题,多线程在调用getInstance,先加锁的线程在修改instance,而后加锁的线程在读数据,这样就会存在内存可见性的安全问题
3、加上volatile,解决内存可见行的问题。
static class Singleton {
private Singleton()</