(一)什么是单例模式
确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实
一个简单的单例模式例子:Singleton1.java
public class Singleton1 {
private static final Singleton1 singleton1 = new Singleton1();
//访问权限为private,该方法不能被外面所访问到
private Singleton1() {
}
public static Singleton1 getSingleton1 () {
return singleton1;
}
}
构造函数为private,则外界将不能通过new创建对象,只能通过调用getSingleton1()方法获得对象,并且这个对象是原来就创建好了的
这一种写法是单例模式中的饿汉式模式,在类加载的过程中就会对这个实例进行初始化,缺点就是不是懒加载的,不是在需要的时候才对它进行初始化
(二)单例模式的优点
1)只创建了一个实例,节省内存开销
2)减少了系统的性能开销
3)避免对资源的多重占用
4)在系统设置全局的访问点,优化和共享资源优化
(三)单例模式的缺点
1)不易于扩展,单例模式没有接口,不过接口单例模式好像是没什么意义的,单例模式的应用场景是生成对象啊,和接口扯不上边,而且接口是不能实例化的
2)对测试不利,因为不能使用mock的方式虚拟一个对象
(四)单例模式的应用场景
spring ioc使用单利模式创建bean,只是知道它是这么做的,因为没有研读spring源码,所以很多细节需要以后来补充
(五)并发条件下的单例模式
单例模式有很多种实现方式
上面只是其中一种,还有其他比如Singleton2.java
public class Singleton2 {
private static Singleton2 singleton2 = null;
private Singleton2() {}
public static Singleton2 getSingleton2() {
if (singleton2 == null) {
singleton2 = new Singleton2();
}
return singleton2;
}
}
Singleton1在并发的情况下是线程安全的,但Singleton2在并发情况下是线程不安全的,很有可能创建多个实例
修改方式:
1)在方法前面加上synchronized关键字,或者使用Lock来实现
public static synchronized Singleton2 getSingleton2() {
if (singleton2 == null) {
singleton2 = new Singleton2();
}
return singleton2;
}
2)同步代码块
public static Singleton2 getSingleton2() {
synchronized(singleton2) {
if (singleton2 == null) {
singleton2 = new Singleton2();
}
}
return singleton2;
}
目前比较熟悉的解决并发创建对象的方法,以后还要继续补充
上面两种虽然解决了线程安全,但并不高效
3)双重检查锁
public class Singleton3 {
private static volatile Singleton3 singleton3 = null;
private Singleton3() {}
public static Singleton3 getSingleton2() {
if (singleton3 == null) {
synchronized (Singleton3.class) {
if (singleton3 == null) {
singleton3 = new Singleton3();
}
}
}
return singleton3;
}
}
为什么需要对singleton3添加volatile修饰符,因为singleton3 = new Singleton3();
不是原子性的,分为三步
1.为singleton3
分配内存
2.调用构造函数进行初始化
3.将singleton3
对象指向分配的内存(执行完这步singleton3将不为null)
为了提高程序的运行效率,编译器会进行一个指令重排,线程1执行到= new Singleton3();
线程2执行到if (singleton3 == null)
假设进行了指令重排,2和3顺序相反,此时singleton3
不为null,则线程2此时可能直接返回未正确进行初始化的singleton3
对象。使用volatile可以禁止指令重排序
4)静态内部类
public class Singleton4 {
private static class SingletonHolder {
private static final Singleton4 singleton4 = new Singleton4();
}
private Singleton4() {}
public static Singleton4 getSingleton4() {
return SingletonHolder.singleton4;
}
}
因为被static修饰,所以只会初始化一次,并且singleton4
只会在访问该字段时初始化一次,所以它是懒汉式的
5)枚举方式
public enum Singleton5 {
INSTANCE;
public void hello() {
System.out.println("hello, successful");
}
}
public class Singleton5Test {
public static void main(String[] args) {
Singleton5 singleton5 = Singleton5.INSTANCE;
singleton5.hello();
}
}
代码相当优美
实现原理:枚举类的域(field)其实是相应的enum类型的一个实例对象
可以参考implementing-singleton-with-an-enum-in-java
(六)单例模式的扩展---多例模式
产生固定数量对象的模式。通过一个计数器实现
感觉自己的积累量还不够,很多东西都没办法想明白,比如这种设计模式的优缺点,为什么是这样的吗,如何解决,还有很长的路要走,厚积而薄发,切勿浮躁!!!
网友评论