对于单例,很多人就要问了。为什么要使用单例,单例意义何在?
单例的产生是由于类的频繁使用,每次生成对象都要new,使用完值后GC要释放对象。这样一来系统性能降低,GC承受着巨大的压力。为了能够提升系统性能或其他,以及减轻GC压力。我们引进了单例模式。
首先我们来看三个经典的单例模式
import java.util.Map;
import java.util.HashMap;
class Single1 {
static Single1 single = null;
private Single1() {
}
public synchronized static Single1 getInstance() {
if(single==null) {
single= new Single1();
}
return single;
}
}
/**
*单例模式类
*@author qxh
*@version 1.0 2015/6/13
*/
class Single2{
private static final Single2 instance;//静态变量存放对象的引用
/**
*私有的构造方法
*
*/
private Single2() {
}
/**
*静态初始化块进行初始化
*
*/
static {
instance=new Single2();
}
/**
*获取单例对象的方法
*@param 无
*@return 存放该对象地址的引用
*/
public static Single2 getInstance() {
return instance;
}
}
class Single3 {
static Map<String,Single3> single_map= new HashMap<String,Single3>();
private Single3() {
}
static {
Single3 single = new Single3();
single_map.put(single.getClass().getName(),single);
}
public static Single3 getInstance(String name) {
if(name==null) {
name = Single3.class.getName();
}
if(single_map.get(name)==null) {
try{
single_map.put(name,(Single3)Class.forName(name).newInstance());
}catch(Exception e) {
}
}
return single_map.get(name);
}
}
class TestThread extends Thread {
public void run() {
long begin=System.currentTimeMillis();
for(int i=1;i<100000;i++)
//Single1.getInstance();//耗时about46
//Single2.getInstance();//耗时0
Single3.getInstance(null);//耗时0
System.out.println("spend: " +(System.currentTimeMillis()-begin));
}
}
public class Single {
public static void main(String[] args) {
TestThread t1 = new TestThread();
TestThread t2 = new TestThread();
TestThread t3 = new TestThread();
TestThread t4 = new TestThread();
TestThread t5 = new TestThread();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
我们先来看下第二种一开始就用初始化块进行单例初始化,这样虽然可以避免多线程下造成的数据混乱但是没有做到延迟加载,可能会使程序初始化较慢,第一种方式为了用了延迟加载,加了锁,虽然做到同步和延迟加载,但为了延迟加载,而牺牲系统系能未免不妥。第三种登记注册式单例,由于使用单例类名作为key而map集合又不可重复再加上构造方法私有,由公共方法同过类名得到的实例有且只有一个。这种方式和第二种一样,做到了同步,却没有做到延迟加载。为此我们的程序还需进一步改进。
public class Single{
private Single() {
}
private static class SingleHolder{
private static final Single single = new Single();
}
public static Single getInstance() {
return SingleHolder.single;
}
}
这种单例使用内部类来维护单例模式,当加载Single类时内部类并不会被加载,从而保证单例类不会被初始化,做到了延迟加载。又由于单例类实例的获得实在内部类加载完(类创建是原子性的非并发)之后才返回的,也就是说第一次初始化调用getInstance()就对单例类对象进行了初始化(类似第一)第二种,所以说对多线程友好。即做到了延迟加载又做到多线程友好。系统初始化和系统性能都能得到提升。