单例模式
2016-03-18 17:29
381 查看
单例模式的定义:
Ensure a class has only one instance,and provide a gloabl point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例)。
实现方法:把构造函数设置为private私有访问权限。然后只允许在本类中调用构造函数。进而达到“单例”。
优点:
1、由于单例模式在内存中只能生成一个实例,减少了内存开支,特别是一个对象需要频繁创建、销毁时,而且创建或销毁又无法优化的时候,单例模式的优势就特别明显。
2、由于单例模式只生成一个实例,所以减少了系统的性能开销,当对一个对象产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动是直接产生一个单例对象,然后永久驻留内存的方式来解决(在JAVA EE中采用单例模式时需要注意JVM垃圾回收机制)。
3、单例模式可以避免对资源的多重占用,例如一个写文件的动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时操作。
4、单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。
缺点:
1、单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本么有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何意义的,它要求“自行实例化”,并且提供单一实例、接口或抽象类是个不可能的杯实例化的。在特殊情况下,单例模式可以实现接口、被继承等,需要根据具体环境判断。
2、单例模式对测试是不利的,在并发开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象。
3、单例模式与单一职责原则有冲突,一个类应该只实现一个逻辑,而不关心他是否是单例的,是不是要单例取决于环境,单例模式把“要单例”和业务逻辑融合在一个类中。
单例模式的通用代码:
(也称为饿汉模式)
单例模式的线程不安全性:
在高并发的情况下,请注意单例模式的线程同步问题。
线程不安全的例子:
public class Singleton {
}
该单例模式在低并发的情况下尚不会出现问题,但在高并发的情况下可能在内存中出现多个实例。为什么会出现这种情况?如果一个线程A执行到singleton = new Singleton();这一步,但是尚未获得对象,而线程B获得的判断条件也是为真。于是继续运行下去,线程A和线程A都获得了一个对象,在内存中就会出现两个对象。
因此,我们可以在采取线程同步。修改如下:
以上代码我们称为懒汉模式
饿汉式是线程安全的,在类创建的同时就已经创建好一个静态的对象供系统使用,以后不在改变
懒汉式如果在创建实例对象时不加上synchronized则会导致对对象的访问不是线程安全的
推荐使用第一种
从实现方式来讲他们最大的区别就是懒汉式是延时加载,
他是在需要的时候才创建对象,而饿汉式在虚拟机启动的时候就会创建,
饿汉式无需关注多线程问题、写法简单明了、能用则用。但是它是加载类时创建实例、所以如果是一个工厂模式、缓存了很多实例、那么就得考虑效率问题,因为这个类一加载则把所有实例不管用不用一块创建。
懒汉式的优点是延时加载、缺点是应该用同步(想改进的话现在还是不可能,比如double-check)、其实也可以不用同步、看你的需求了,多创建一两个无引用的废对象其实也没什么大不了。
单例模式的拓展:重点内容
一个类可以产生多个对象,这很容易实现,直接new就可以了,一个类只产生一个对象,我们可以采取单例模式。但是,如果我们需要两,三个对象,该怎么办了?
实现如下:
client端代码:
终端输出为:
第1次访问的对象是
对象2
第2次访问的对象是
对象1
第3次访问的对象是
对象0
第4次访问的对象是
对象2
第5次访问的对象是
对象0
通过以上代码我们便实现了一个类中有好几个(两三个)对象。
Ensure a class has only one instance,and provide a gloabl point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例)。
实现方法:把构造函数设置为private私有访问权限。然后只允许在本类中调用构造函数。进而达到“单例”。
优点:
1、由于单例模式在内存中只能生成一个实例,减少了内存开支,特别是一个对象需要频繁创建、销毁时,而且创建或销毁又无法优化的时候,单例模式的优势就特别明显。
2、由于单例模式只生成一个实例,所以减少了系统的性能开销,当对一个对象产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动是直接产生一个单例对象,然后永久驻留内存的方式来解决(在JAVA EE中采用单例模式时需要注意JVM垃圾回收机制)。
3、单例模式可以避免对资源的多重占用,例如一个写文件的动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时操作。
4、单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。
缺点:
1、单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本么有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何意义的,它要求“自行实例化”,并且提供单一实例、接口或抽象类是个不可能的杯实例化的。在特殊情况下,单例模式可以实现接口、被继承等,需要根据具体环境判断。
2、单例模式对测试是不利的,在并发开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象。
3、单例模式与单一职责原则有冲突,一个类应该只实现一个逻辑,而不关心他是否是单例的,是不是要单例取决于环境,单例模式把“要单例”和业务逻辑融合在一个类中。
单例模式的通用代码:
(也称为饿汉模式)
public class Singleton{ private static final Singleton singleton = new Singleton(); private Singleton (){ } public static void main(args String[]){ return singleton; } public static void doSomething(){ } }
单例模式的线程不安全性:
在高并发的情况下,请注意单例模式的线程同步问题。
线程不安全的例子:
public class Singleton {
private static Singleton singleton = null; private Singleton(){ } public static Singleton getSingleton(){ if(singleton == null){ singleton = new Singleton(); } return singleton; }
}
该单例模式在低并发的情况下尚不会出现问题,但在高并发的情况下可能在内存中出现多个实例。为什么会出现这种情况?如果一个线程A执行到singleton = new Singleton();这一步,但是尚未获得对象,而线程B获得的判断条件也是为真。于是继续运行下去,线程A和线程A都获得了一个对象,在内存中就会出现两个对象。
因此,我们可以在采取线程同步。修改如下:
public class Singleton{ private static Singleton singleton = null; public static synchronized synchronized getInstance(){ if(singleton==null){ singleton = new Singleton(); } return singleton; } }
以上代码我们称为懒汉模式
饿汉式是线程安全的,在类创建的同时就已经创建好一个静态的对象供系统使用,以后不在改变
懒汉式如果在创建实例对象时不加上synchronized则会导致对对象的访问不是线程安全的
推荐使用第一种
从实现方式来讲他们最大的区别就是懒汉式是延时加载,
他是在需要的时候才创建对象,而饿汉式在虚拟机启动的时候就会创建,
饿汉式无需关注多线程问题、写法简单明了、能用则用。但是它是加载类时创建实例、所以如果是一个工厂模式、缓存了很多实例、那么就得考虑效率问题,因为这个类一加载则把所有实例不管用不用一块创建。
懒汉式的优点是延时加载、缺点是应该用同步(想改进的话现在还是不可能,比如double-check)、其实也可以不用同步、看你的需求了,多创建一两个无引用的废对象其实也没什么大不了。
单例模式的拓展:重点内容
一个类可以产生多个对象,这很容易实现,直接new就可以了,一个类只产生一个对象,我们可以采取单例模式。但是,如果我们需要两,三个对象,该怎么办了?
实现如下:
package Test; import java.util.ArrayList; import java.util.Random; public class SingletonExpand { //最大生产对象数量 private static int maxNum = 3; //用于存储对象的名字 private static ArrayList<String> nameLists = new ArrayList<String>(); //用于存储对象 private static ArrayList<SingletonExpand> singletonlist = new ArrayList<SingletonExpand>(); //当前对象序列号 private static int countNum = 0; //产生所有的对象 static{ for(int i =0; i < maxNum ;i++){ singletonlist.add(new SingletonExpand("对象" + i)); } } //传入对象名,用来辅助对象的创建 private SingletonExpand(String name){ nameLists.add(name); } public static SingletonExpand getSingleton(){ Random random = new Random(); countNum = random.nextInt(maxNum); return singletonlist.get(countNum); } public void info(){ System.out.println(nameLists.get(countNum)); } }
client端代码:
public class client { public static void main(String[] args) { // TODO Auto-generated method stub for(int i=0 ; i< 5;i++){ SingletonExpand s = SingletonExpand.getSingleton(); System.out.println("第"+(i+1)+"次访问的对象是"); s.info(); } } }
终端输出为:
第1次访问的对象是
对象2
第2次访问的对象是
对象1
第3次访问的对象是
对象0
第4次访问的对象是
对象2
第5次访问的对象是
对象0
通过以上代码我们便实现了一个类中有好几个(两三个)对象。
相关文章推荐
- 设计模式之单例模式
- 【设计模式-单例模式】
- 文章标题
- 单例模式
- 单例模式几种实现方式
- 设计模式-单例模式
- java单例模式的七种写法
- 单例模式 singleton;
- bless
- Activity和Fragment
- 前端 页面无刷新方案二
- struts2
- Android 屏幕适配(一)百分比布局库(percent-support-lib) 解析与扩展
- Android加密方式的实现代码MD5-RSA-DES
- C#中的ref和out
- 实战--通过dockerfile本地编译tomcat镜像
- 修改Android Studio的代码补全快捷键,解决与输入法快捷键冲突
- Java synchronized
- NSURLProtocol和NSRunLoop的那些坑
- CentOS如何挂载硬盘