单例模式

Posted cleveraboy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式相关的知识,希望对你有一定的参考价值。

单例模式是一种创建性模式,是一种创建对象的方式,主要指的就是一个类在整个程序的运行的过程中对象只会创建一个实例,就是只会被new一次,然后下次访问这个对象的时候是不用重新创建对象的。官方的解释是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式有很多种写法,比如:懒汉式(线程安全/线程不安全)、饿汉式、枚举式、序列化式、内部类式、注册式、注册登记式、双重检查锁(DCL)等等。

下面使用穷举法举几个例子:

1.spring源码中的单例,可以使用getBean返回不同的对象。

2.咱们程序里面多线程同时操作一个txt文件时会出现错误,这个文件的创建可能是一个单例。

3.还有比如太阳、月亮、人等等应该也是可以说成是单例的,因为对象只有一个,所有人看到的是同一个太阳、月亮。

4.还有日常生活中的日历。

饿汉式

package pattern.single;
public class Hungry {
    private Hungry(){ }
    private static final Hungry hungry=new Hungry();
    public static Hungry getInstance(){
        return hungry;
    }
}
package pattern.single;
public class SingleTest {
    public static void main(String[] args) {
       Hungry hungry=Hungry.getInstance();
       Hungry hungry1=Hungry.getInstance();
       System.out.println(hungry==hungry1);
    }
}

饿汉式的优点是性能比较高,没有现成安全问题,还是对象在初始化的时候就已经创建,不管会不会用到都得创建,所以会有浪费资源。

懒汉式线程不安全

package pattern.single;
public class LazyOne {
    private LazyOne(){}
    private static LazyOne lazy=null;
    public static LazyOne getInstance(){
        if(lazy==null){
            lazy=new LazyOne();
        }
        return lazy;
    }
}
package pattern.single;
public class SingleTest {
    public static void main(String[] args) {
        LazyOne lazyOne=LazyOne.getInstance();
        System.out.println(lazyOne); 
    }
}

这种方法只能在单线程的环境下使用,在多线程情况下存在线程安全问题,但是只会在用到的时候创建对象。

懒汉式线程安全

package pattern.single;
public class LazyTwo {
    private LazyTwo(){}
    private static LazyTwo lazy=null;
    public static synchronized LazyTwo getInstance(){
        if(lazy==null){
            lazy=new LazyTwo();
        }
        return lazy;
    }
}
package pattern.single;
public class SingleTest {
    public static void main(String[] args) {
        LazyTwo lazyTwo=LazyTwo.getInstance();
        System.out.println(lazyTwo); 
    }
}

登记式静态内部类

package pattern.single;
public class LazyThree {
    private static boolean initialized=false;
    public LazyThree(){
       synchronized (LazyThree.class){
          if(initialized==false){
            initialized=!initialized;
          }else{
              throw new RuntimeException("单例被攻击");
          }
       }
    }
    public static final LazyThree getInstance(){
        return LazyHolder.Lazy;
    }
    private static class LazyHolder{
        private final static LazyThree Lazy=new LazyThree();
    }
}
package pattern.single;
public class SingleTest {
    public static void main(String[] args) {   
        LazyThree lazyThree=LazyThree.getInstance();
        System.out.println(lazyThree);
    }
}

这种方式的效率比懒汉式线程安全的模式更高,同时在初始化的时候也不会加载,避免了资源的浪费。

枚举式

package pattern.single;
public enum RegisterEnum {
    BLOCK(){
        private int r=0;
        private int g=0;
        private int b=0;
    },RED(){
        private int r=255;
        private int g=0;
        private int b=0;
    };
}
package pattern.single;
public class SingleTest {
    public static void main(String[] args) {
        RegisterEnum registerEnum=RegisterEnum.BLOCK;
        System.out.println(registerEnum);
    }
}

序列化式

package pattern.single;
import java.io.Serializable;
public class Seriable implements Serializable {
    public final static Seriable INSTANCE=new Seriable();
    private Seriable(){}
    public static Seriable getInstance(){
        return INSTANCE;
    }
    private Object readObject(){
        return INSTANCE;
    }
}
package pattern.single;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SingleTest {
    public static void main(String[] args) {
        Seriable s1 = null;
        Seriable s2 = Seriable.getInstance();
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(s2);
            oos.flush();
            oos.close();
            FileInputStream fis = new FileInputStream("obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            s1 = (Seriable)ois.readObject();
            ois.close();
            System.out.println(s1);
            System.out.println(s2);
            System.out.println(s1 == s2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注册式

package pattern.single;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class BeanFactory {
    private BeanFactory(){}
    private static Map<String,Object> ioc=new ConcurrentHashMap<>();
    public static Object getBean(String className) throws Exception {
       if(ioc.containsKey(className)){
           return ioc.get(className);
       }else{
           ioc.put(className,Class.forName(className).newInstance());
       }
       return ioc.get(className);
    }
}
package pattern.single;
public class SingleTest {
    public static void main(String[] args) {
        Object obj=BeanFactory.getBean("xxx.yyy");
System.out.println(obj);
}
}

双重检查锁(DCL)

package pattern.single;
public class Singleton {
    private volatile static Singleton singleton;
    private Singleton(){}
    public static Singleton getInstance(){
        if(singleton==null){
           synchronized (Singleton.class){
              if(singleton==null){
                 singleton=new Singleton();
              }
           }
        }
        return singleton;
    }
}
package pattern.single;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SingleTest {
    public static void main(String[] args) {
        Object obj=Singleton.getInstance();
        System.out.println(obj);
    }
}

这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

 




以上是关于单例模式的主要内容,如果未能解决你的问题,请参考以下文章

常用代码片段

性能比较好的单例写法

片段作为 Android 中的单例

单例片段或保存网页视图状态

你熟悉的设计模式都有哪些?写出单例模式的实现代码

单例模式以及静态代码块