单例设计模式
概述
单例设计模式注意要用三种:懒汉式单例、饿汉式单例、登记式单例。
特点:
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一的实例
- 单例类必须给所有其他对象提供这一实例
代码实例
一:懒汉式单例
public class Singleton{
private Singleton(){}
private static Singleton single = null;
public static Singleton getInstance(){
if(single == null){
single = new Singleton();
}
return single;
}
}
因为以上代码存在线程不安全问题,此处在getInstance方法上添加同步
public static synchronized Singleton getInstance(){
if(single == null){
single = new Singleton();
}
return single;
}
双重检查锁定
public static Singleton getInstance(){
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
静态内部类
public class Singleton {
private static class LazyHolder{
private static final Singleton INSTANCE = new Singleton();
}
private Singleton(){}
public static final Singleton getInstacen(){
return LazyHolder.INSTANCE;
}
}
此种方法既实现了线程安全,又避免了同步带来的性能影响
在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的, |
---|
在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗 |
利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,一般倾向于使用这一种。 |
二:饿汉式单例
public class Singleton{
private Singleton(){}
private static final Singleton single = new Singleton();
public static Singleton getInstance(){
return single;
}
}
饿汉式在类创建的时候就已经创建好了一个静态的对象供系统使用,以后不再改变,所以天生线程安全。
三:登记式单例(了解)
public class Singleton{
private static Map<String,Singleton> map = new HashMap<String,Singleton>();
static{
Singleton single = new Singleton();
map.put(single.getClass().getName(),single);
}
protected Singleton(){}
public static Singleton getInstance(String name){
if(name == null){
name = Singleton.class.getName();
System.out.println("name == null" + "--->" + name);
}
if(map.get(name) == null){
try{
map.put(name,(Singleton)Class.forName(name).new Instance());
}catch(Exception e){
e.printStackTrace();
}
}
return map.get(name);
}
//一个示意性的商业方法
public String about(){
retrun "hello ";
}
public static void main(String[] args){
Singleton single = Singleton.getInstance(null);
System.out.println(single.about());
}
}
登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map中,对于已经登记过的实例,则从Map中直接返回,没有等级的,先登记然后返回。
饿汉式与懒汉式的区别
饿汉式 | 懒汉式 | |
---|---|---|
类加载时 | 此时单例初始化完成 | 只有当调用getInstance时才初始化这个单例 |
线程安全 | 天生线程安全,可以直接用于多线程而不出现问题 | 天生非线程安全 |
资源加载和性能 | 类创建时就会创建单例,占据一定内存,此后的调用速度也更快 | 延迟加载,调用后就和饿汉式相同 |
应用
public class TestSingleton{
String name = null;
private TestSingleton(){}
private static volatile TestSingleton instance = null;
public static TestSingleton getInstance(){
if(instance == null){
synchronized (TestSingleton.class){
if(instance == null){
instance = new TestSingleton();
}
}
}
return instance;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public void printInfo(){
System.out.println("the name is " + name);
}
}
单例的优缺点
优点:
1)由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
(2)由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,比如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决。
(3)单例模式可以避免对资源的多重占用,例如一个写文件操作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
(4)单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如,可以设计一个单例类,负责所有数据表的映射处理。
缺点:
(1)单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。
(2)单例对象如果持有Context,那么很容易引发内存泄露,此时需要注意传给单例对象的Context最好是Application Context。