单例模式
单例模式
单例模式是一种常见的设计模式,它保证了一个类只有一个唯一的对象,提供一个全局的访问点,例如,我们只需要用一个对象去管理配置文件,这时候就可以用单例模式。
单例模式的分类
饿汉式单例
1.静态常量法
//饿汉单例,线程安全
public class Hungry {
//构造器私有
private Hungry(){
}
private static final Hungry hungry = new Hungry();
//如果没被使用,这种情况会存在内存浪费
private int[] arr = new int[1024];
public static Hungry getInstance(){
return hungry;
}
}
2.静态代码块
public class HungryBlock {
private static HungryBlock hungryBlock;
private HungryBlock() {
}
static {
hungryBlock = new HungryBlock();
}
public static HungryBlock getInstance() {
return hungryBlock;
}
}
3.静态内部类
public class HungryInner {
private HungryInner() {
}
public static class HungryInner2{
private static final HungryInner hungryInner = new HungryInner();
}
public static HungryInner getInstance() {
return HungryInner2.hungryInner;
}
}
懒汉式单例
等到真正使用的时候才去创建实例,不用时不去主动创建。
1.判断一次且不加锁,存在线程安全问题
//懒加载,会存在线程安全问题
public class lazyInstance {
private static lazyInstance obj = null;
private lazyInstance() {
}
public static lazyInstance getInstance() {
if(obj == null) {
obj = new lazyInstance();
}
return obj;
}
}
2.对getInstance加上synchronized关键字,线程安全,每次获取对象时都会加锁,性能低下
public class lazyInstance {
private static lazyInstance obj = null;
private lazyInstance() {
}
public static lazyInstance getInstance() {
if(obj == null) {
obj = new lazyInstance();
}
return obj;
}
}
3.用synchronized对类加同步锁+两次检查,也会存在线程安全问题,因为 obj = new lazyInstance();不具有原子性,正常情况下是先分配内存空间,再初始化对象,最后obj指向对象的内存空间,但最后两步可能存在指令重排序,导致obj先指向内存空间,从而使得其他线程获取到空对象。
public class lazyInstance {
private static lazyInstance obj = null;
private lazyInstance() {
}
public static lazyInstance getInstance() {
if(obj == null) {
synchronized(lazyInstance.class) {
if(obj == null) {
obj = new lazyInstance();
}
}
}
return obj;
}
}
4.双重校验+volatile,这种写法可以实现单例,但是在特殊情况下也会存在问题,因为反射可以无视类的私有构造方法,创造出新对象。
public class lazyInstance {
private static volatile lazyInstance obj = null;
private lazyInstance() {
}
public static lazyInstance getInstance() {
if(obj == null) {
synchronized(lazyInstance.class) {
if(obj == null) {
obj = new lazyInstance();
}
}
}
return obj;
}
}
例如
public class LazyInstance {
private static volatile LazyInstance obj;
private LazyInstance() {
synchronized (LazyInstance.class){
if(obj!=null){
throw new RuntimeException("对象已存在");
}
}
}
public static LazyInstance getInstance() {
if(obj == null) {
synchronized(LazyInstance.class) {
if(obj == null) {
obj = new LazyInstance();
}
}
}
return obj;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
LazyInstance instance = LazyInstance.getInstance();
//获取空参构造器哦
Constructor<LazyInstance> constructor = LazyInstance.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
LazyInstance instance2 = constructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
利用反射可以生成一个可以和getInstance()获取的不一样的instance2对象,当然也可以解决,在构造器里面加上校验即可,上面代码已经给出。
不加校验结果:

加校验结果:

5.枚举创建单例(最完美)
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}

浙公网安备 33010602011771号