一、简介
单例模式是设计模式中使用最普遍的一种设计模式,它是对象创建模式,用于产生一个对象的具体实例,
确保系统中一个类只有一个实例。
二、使用场景及优点
对于频繁使用的对象,可以省略创建对象花费的时间。同时由于new操作的吃书减少,因此对系统内存
的使用频率会降低,减轻了GC压力,缩短了GC停顿时间。
三、常见表现形式
1.在类加载时初始化单例属性(优点:效率较高,多线程安全;缺点:增多程序加载时的额外负载)
public class ServiceConnation1
{
/**
* 类加载时就实例化单例属性,此策略较为安全,多线程时不会产生多个实例。但是当被实例化的
* 类的构造方法需要做大量工作、或做者远程连接操作就会使得程序在启动时耗去很长的时间,致
* 使启动时间延长,用户体验降低。
*/
private static ServiceConnation1 sc = new ServiceConnation1();
private InitialContext context;
private ServiceConnation1()
{
try
{
/**
* 此处并没有指定properties属性,只是示例简要的结构,模拟远程连接操作等
* 较为耗时的初始化代码,程序并不能运行成功。
*/
context = new InitialContext();
}
catch (NamingException e)
{
e.printStackTrace();
}
}
public static ServiceConnation1 getInstance()
{
return sc;
}
public Object getService(String name) throws NamingException
{
return context.lookup(name);
}
}
2.延迟加载策略(优点:减少程序加载时的负载;缺点:需要引入同步,效率低下)
public class ServiceConnation2
{
private static ServiceConnation2 sc = null;
private InitialContext context;
private ServiceConnation2()
{
try
{
/**
* 此处并没有指定properties属性,只是示例简要的结构,模拟远程连接操作等
* 较为耗时的初始化代码,程序并不能运行成功。
*/
context = new InitialContext();
}
catch (NamingException e)
{
e.printStackTrace();
}
}
public static synchronized ServiceConnation2 getInstance()
{
/**
* 在调用方法是判断是否初始化单例对象,这样可以很好的解决上述启动耗时的问题,方法在不添
* 加同步关键字的时候,在非多线程运行下较为可靠,效率也较高。但是在多线程的情况下可能会
* 产生多个实例,因此需要给方法添加synchronized修饰,这样效率也大大降低了。
*/
if(sc == null){
sc = new ServiceConnation2();
}
return sc;
}
public Object getService(String name) throws NamingException
{
return context.lookup(name);
}
}
3.内部类延迟加载策略(改进前两种的缺点,推荐使用)
public class ServiceConnation3
{
private InitialContext context;
private ServiceConnation3()
{
try
{
/**
* 此处并没有指定properties属性,只是示例简要的结构,模拟远程连接操作等
* 较为耗时的初始化代码,程序并不能运行成功。
*/
context = new InitialContext();
}
catch (NamingException e)
{
e.printStackTrace();
}
}
public static ServiceConnation3 getInstance()
{
/**
* 为了解决因延长加载而带来的同步效率低下的问题,将程序改进为内部类的方式实例化单例对象
*/
return ServiceConnationHolder.sc;
}
public Object getService(String name) throws NamingException
{
return context.lookup(name);
}
/**
* ServiceConnationHolder为内部类,在程序初始化的收不会被加载,因此利用内部类的
* 这种特性可以实现延迟加载,同时又不存在多线程安全带的问题。
*/
private static class ServiceConnationHolder
{
private static ServiceConnation3 sc = new ServiceConnation3();
}
}
四、测试
public class ServiceConnation
{
private ServiceConnation()
{
try
{
System.out.println("************do inital something************");
Thread.sleep(2000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public static ServiceConnation getInstance()
{
return ServiceConnationHolder.instance;
}
public Object getService(String name) throws NamingException
{
System.out.println("************getService************");
return new Object();
}
private static class ServiceConnationHolder{
public static ServiceConnation instance = new ServiceConnation();
}
}
public class SingletonTest
{
@Test
public void testInitalSingleton(){
ServiceConnation sc = ServiceConnation.getInstance();
try
{
sc.getService("beanName1");
sc = ServiceConnation.getInstance();
sc.getService("beanName2");
}
catch (NamingException e)
{
e.printStackTrace();
}
}
}