什么是单例模式?
单例模式(Singleton Pattern) 是指一个类在任何情况下只有一个实例,全局只有一个访问点
隐藏其所有的构造方法,属于创建型模式
创建型模式都有那些:工厂模式,单例模式,建造者模式和原型模式
单例模式的写法和优缺点:
1、饿汉单例模式
在类加载的时候立即初始化,并且创建单例对象,它绝对线程安全,在线程还没有出现之前就实例化了,不可能存在访问安全问题。
实例代码如下:
public class HungryStaticSingleton implements Serializable{
private static final HungryStaticSingleton singleton;
static {
singleton = new HungryStaticSingleton();
}
private HungryStaticSingleton(){};
public static HungryStaticSingleton getInstance(){
return singleton;
}
private Object readResolve(){
return singleton;
}
}
优点:高性能,安全
缺点:在需要比较多的单例对象时,不管用或不用都会创建,浪费内存
2、懒汉单例模式
对象在使用的时候被初始化
public class LazySimpleSingleton {
//静态块,公共内存区域
private static LazySimpleSingleton singleton;
private LazySimpleSingleton(){};
public static LazySimpleSingleton getInstance(){
if(singleton == null){
singleton = new LazySimpleSingleton();
}
return singleton;
}
}
优点:不浪费内存
缺点:非线程安全
当两个Thread线程同时去创建单例时,singleton 都是null singleton == null 就会
3、双重检查锁单例模式
加synchronized相当于在getInstance() 方法中加锁,在多个线程进行时,如果有一个线程抢占锁,其它线程等待,线程安全
代码如下:
public class LazyDoubleCheckSingleton {
private static volatile LazyDoubleCheckSingleton singleton;
private LazyDoubleCheckSingleton(){};
public static LazyDoubleCheckSingleton getInstance(){
if(singleton == null){
synchronized (LazyDoubleCheckSingleton.class){
//检查是否重新创建实例
if(singleton == null){
singleton = new LazyDoubleCheckSingleton();
}
}
}
return singleton;
}
}
优点:安全
缺点: synchronized加锁,性能会有受影响
4、静态内部类单例模式
根据java内部类的特性,调用时加载,避免了内存浪费和synchronized性能下降问题
代码如下:
public class LazyStaticInnerClassSingleton {
//
private LazyStaticInnerClassSingleton(){
};
//static 是为了使单例的空间共享,保证这个方法不会被重写或者重载
public static LazyStaticInnerClassSingleton getInstance(){
//返回结果之前一定要加载内部类
return LazyHolder.INSTANCE;
}
//利用java语法特点,默认不加载内部类
private static class LazyHolder{
private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
}
}
优点:避免了内存浪费和synchronized性能下降问题
缺点:反射可以破坏此单例
优化代码如下:
public class LazyStaticInnerClassSingleton {
//
private LazyStaticInnerClassSingleton(){
if(LazyHolder.INSTANCE != null){
throw new RuntimeException("不允许创建多个实例!");
}
};
//static 是为了使单例的空间共享,保证这个方法不会被重写或者重载
public static LazyStaticInnerClassSingleton getInstance(){
//返回结果之前一定要加载内部类
return LazyHolder.INSTANCE;
}
//利用java语法特点,默认不加载内部类
private static class LazyHolder{
private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
}
}
5、反射破坏单例模式的情况和处理方法
public static void lazyInnerReflex(){
//如果有人恶意用反射来构建对象
Class<?> clazz = LazyStaticInnerClassSingleton.class;
try {
//通过反射获取私有化方法
Constructor<?> c = clazz.getDeclaredConstructor(null);
//强制访问
c.setAccessible(true);
//强制初始化
Object o1 = c.newInstance();
//调用两次构造方法,相当于new两次,犯了原则性问题
Object o2 = c.newInstance();
System.out.println(o1 == o2);
} catch (Exception e) {
e.printStackTrace();
}
}
未优化前结果:
优化后结果:
6、枚举单例模式
代码如下:
public enum EnumSingleton {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}
public static void enumTest(){
EnumSingleton enumSing = EnumSingleton.getInstance();
enumSing.setData(new Object());
EnumSingleton enumSingleton2 = EnumSingleton.getInstance();
enumSingleton2.setData(new Object());
System.out.println(enumSing.getData());
System.out.println(enumSingleton2.getData());
System.out.println(enumSing == enumSingleton2);
}
优点:具备内部类单例的优点,不被反射破坏
测试1:
public static void enumClassTest(){
try {
Class clazz = EnumSingleton.class;
Object o1 = clazz.newInstance();
System.out.println(o1);
}catch (Exception e){
e.printStackTrace();
}
}
测试1结果:
测试2:
测试2结果:
7、序列化和反序列化破坏单例的情况和处理方法
public static void enumSingletonTest(){
HungryStaticSingleton instance1 = null;
HungryStaticSingleton instance2 = HungryStaticSingleton.getInstance();
// instance2.setData(new Object());
try {
FileOutputStream fos = new FileOutputStream("EnumSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(instance2);
oos.flush();
fos.close();
FileInputStream fis =new FileInputStream("EnumSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
instance1 = (HungryStaticSingleton) ois.readObject();
ois.close();
System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance1 == instance2);
}catch (Exception e){
e.printStackTrace();
}
}
HungryStaticSingleton 无readResolve()方法的结果:
HungryStaticSingleton 有readResolve()的结果:
8、容器式单例
虽然枚举式单例写法更加优雅,但是也会存在一些问题。因为它在类加载时将所有的对象初始化都放在类内存中,这其实和饿汉式单例写法并无差异,不适合大量创建单例对象的场景。接下来看注册式单例模式的另一种写法,即容器式单例写法
public class ContainerSingleton {
private ContainerSingleton(){};
private static final Map<String,Object> map = new ConcurrentHashMap<>();
public static Object getBean(String className){
synchronized (ContainerSingleton.class){
if(!map.containsKey(className)){
Object obj = null;
try {
obj = Class.forName(className).newInstance();
map.put(className,obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}else {
return map.get(className);
}
}
}
}
9、ThreadLocal线程单例
代码如下:
public class ThreadLocalSingleton {
private ThreadLocalSingleton(){};
private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
new ThreadLocal<ThreadLocalSingleton>(){
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
public static ThreadLocalSingleton getInstance(){
return threadLocalInstance.get();
}
}
测试:
public static void threadLocalTest(){
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
Thread t1 = new Thread(new ExectorThread());
Thread t2 = new Thread(new ExectorThread());
t1.start();
t2.start();
System.out.println("End");
}
测试结果如下:
参考链接: