1、饿汉式
/**
* Comments: 饿汉式单例,项目启动就创建,执行效率高,
* 不管用不用都先创建,所以比较消耗系统资源(内存浪费)
*
* ProjectName: parent
*
* @author camel
* @date 2020/9/13 16:10
*/
public class HungrySingleton {
private static final HungrySingleton hungrySingleton;
/**
* 隐藏构造方法,防止外部创建实例
*/
private HungrySingleton() {}
static {
hungrySingleton = new HungrySingleton();
}
public HungrySingleton getInstance() {
return hungrySingleton;
}
}
2.1 懒汉式-加锁双重判断
/**
* Comments: 懒汉式单例,延迟创建,需要的时候才创建,效率降低了
* 加锁了,所以性能降低了
*
* ProjectName: parent
*
* @author camel
* @date 2020/9/13 16:15
*/
public class LazySingleton {
/** 加上volatile是防止指令重排序 导致线程混乱 */
private volatile static LazySingleton lazySingleton;
/**
* 隐藏构造方法,防止外部创建实例
*/
private LazySingleton() {}
/**
* 优化后的代码,双重检查,减小加锁导致的性能降低
*
* @return LazySingleton
*/
public static LazySingleton getInstance() {
// 第一次检查是否需要阻塞
if (lazySingleton == null) {
synchronized (LazySingleton.class) {
// 第二次检查是否需要创建对象
if (lazySingleton == null) {
// 上面声明lazySingleton的时候已经分配地址,
// 这里需要重新指向新的地址,防止指令重排序导致的
// 线程混乱,lazySingleton属性加上volatile关键字
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
}
2.2 懒汉式-内部静态类写法
/**
* Comments: 懒汉式静态内部类的写法
* 利用了java本身语法的特点,性能高,避免了内存浪费
* 类文件加载是xxxx.class字节码的形式,内部静态类是xxxx.$LazyInstance .class的形式
* 只有在初始化 类的时候才会加载静态类,所以是懒汉式
*
* ProjectName: parent
*
* @author camel
* @date 2020/9/13 17:03
*/
public class LazyStaticInnerClassSingleton {
private LazyStaticInnerClassSingleton() {
// 防止反射 破坏单例
if (LazyInstance.INSTANCE != null) {
throw new RuntimeException("不允许非法创建");
}
}
public LazyStaticInnerClassSingleton getInstance() {
return LazyInstance.INSTANCE;
}
private static class LazyInstance {
private final static LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
}
}
反射破坏单例
/**
* Comments: 反射测试
*
* ProjectName: parent
*
* @author camel
* @date 2020/9/13 17:13
*/
public class ReferTest {
public static void main(String[] args) {
try {
Class<?> clazz = LazyStaticInnerClassSingleton.class;
Constructor<?> constructor = clazz.getDeclaredConstructor(null);
constructor.setAccessible(true);
Object instance = constructor.newInstance();
System.out.println(instance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
单例是会被反射破坏的,除了将构造方法私有化之外还需要在构造方法里面加判断,初始化了就不能再创建。但是枚举在jdk底层 防止了反射,所以枚举是线程安全的。
3、枚举式
/**
* Comments: 注册(枚举)式单例
* 因为枚举类底层限制了通过反射创建枚举类,所以反射无法破坏枚举式单例
*
* ProjectName: parent
*
* @author camel
* @date 2020/9/13 17:42
*/
public enum EnumSingleton {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Object getInstance() {
return INSTANCE;
}
}
4、容器注册式-spring里面的ioc就是这样
/**
* Comments: 注册式单例,spring中就是用这种容器管理Bean
* ProjectName: parent
*
* @author camel
* @date 2020/9/13 20:44
*/
public class ContainerSingleton {
private ContainerSingleton() {}
private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();
public static Object getInstance(String className) {
synchronized (ioc) {
Object instance = null;
if (!ioc.containsKey(className)) {
try {
instance = Class.forName(className).newInstance();
ioc.put(className, instance);
} catch (Exception e) {
e.printStackTrace();
}
return instance;
} else {
return ioc.get(className);
}
}
}
}
除了反射可以破坏单例,序列化与反序列化也可以破坏单例,如下例子:
5.1用饿汉式单例举例
/**
* Comments: 序列化与反序列化
* ProjectName: parent
*
* @author camel
* @date 2020/9/13 21:15
*/
public class SerializableSingleton implements Serializable {
private static final SerializableSingleton hungrySingleton;
/**
* 隐藏构造方法,防止外部创建实例
*/
private SerializableSingleton() {}
static {
hungrySingleton = new SerializableSingleton();
}
public static SerializableSingleton getInstance() {
return hungrySingleton;
}
}
5.2 - 测试类
/**
* Comments: TODO
* ProjectName: parent
*
* @author camel
* @date 2020/9/13 21:14
*/
public class SerializableSinglertonTest {
public static void main(String[] args) {
SerializableSingleton s1 = null;
SerializableSingleton s2 = SerializableSingleton.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("SeriableSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (SerializableSingleton)ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
序列化与反序列化破坏单例的解决方法:加个方法readResolve()
/**
* Comments: 序列化与反序列化
* ProjectName: parent
*
* @author camel
* @date 2020/9/13 21:15
*/
public class SerializableSingleton implements Serializable {
private static final SerializableSingleton hungrySingleton;
/**
* 隐藏构造方法,防止外部创建实例
*/
private SerializableSingleton() {}
static {
hungrySingleton = new SerializableSingleton();
}
public static SerializableSingleton getInstance() {
return hungrySingleton;
}
/**
* 防止反序列化破坏单例
*
* @return Object
*/
public Object readResolve() {
return hungrySingleton;
}
}
6 - ThreadLocal方式
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
new ThreadLocal<ThreadLocalSingleton>(){
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton(){}
public static ThreadLocalSingleton getInstance(){
return threadLocalInstance.get();
}
}