使用场景:
当使用一个类会消耗很大的系统资源时,没有必要去创建多个对象,这时候我们就应该使用单例模式。而为了使创建的对象有且只有一个,类的构造方法应该是私有的,且获取实例的方法应该是线程安全的。
单例的几种实现方式:
1.饿汉模式
public class Test {
private static Test signalIns = new Test();
private Test()
{
}
public static Test getIns()
{
return signalIns;
}
}
饿汉模式是在声明的时候就已经初始化了一个对象,假如没有使用的话这个对象也一直存在。
2.懒汉模式
public class Test {
private static Test signalIns;
private Test()
{
}
public synchronized static Test getIns()
{
if (null == signalIns)
{
signalIns = new Test();
}
return signalIns;
}
}
懒汉模式相对于饿汉模式来说是在调用getIns的时候才会去初始化对象,但是由于synchronized导致每次调用getIns时都进行了同步,造成了不必要的开销。
public static Test getIns()
{
if (null == signalIns)
{
synchronized (Test.class)
{
if (null == signalIns)
{
signalIns = new Test();
}
}
}
return signalIns;
}
这是改良后的懒汉模式,这种方式相对于上一种的优点是在调用getIns的时候并不会每次都进行同步。
懒汉模式在高并发环境下还是有一定的缺陷的。在jdk1.5之后,可以在声明signalIns的时候用volatile修饰,保证每次都从主内存去读取。
静态内部类模式
public static Test getIns()
{
return SignalIns.signalIns;
}
private static class SignalIns
{
private static final Test signalIns = new Test();
}
这种方式不仅延迟了单例的实例化,并且不会导致线程不安全。
一般单例模式常用的就是以上几个方法,下面再记录下单例的另外两种:枚举和使用容器。
枚举模式
public static enum SignalIns
{
SIGNALINS;
public void function()
{
}
}
定义一个枚举的元素即为一个实例。之前几中模式在使用反序列化操作或者反射时都可以创建一个新的实例,而枚举有效地避免了。
容器实现单例
private static Map<String, Object> insMap = new HashMap<>();
public static void holdIns(String key, Object Ins)
{
if (!insMap.containsKey(key)) {
insMap.put(key, Ins);
}
}
public static Object getIns(String key)
{
return insMap.get(key);
}
在程序开始时将多个单例对象放入一个管理类中。