下图是单例模式的一个简单类图:
通过静态方法getInstance获取对象实例,一种方式是通过static初始化模块来实例化这个单例对象,这种方式在该类被加载时实例,由于类的初始化过程是唯一的所以这种单例模式也是线程安全的。
public class OneClass { private static OneClass instance=new OneClass(); private OneClass(){ } public static OneClass getInstance(){ return instance; } public void func(){ System.out.println(this.toString()); } public static void main(String args[]){ OneClass one=OneClass.getInstance(); one.func(); } }
另外一种方式在第一次获取getInsatnce时进行实例化,这种方式明显在多线程情况下需要对getInstance进行加锁控制。下面这段代码是未对getInstance进行加锁,由于构造函数的2秒暂停,可以发现最终结果子线程和main线程打印出的对象id是不同的。解决的方法很简单对getInstance方法添加synchronized关键字即可。
public class OneClass { private static OneClass instance=null; private OneClass(){ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } public static OneClass getInstance(){ if(instance==null){ instance=new OneClass(); } return instance; } public void func(){ System.out.println(this.toString()); } public static void main(String args[]){ new Thread(new Runnable(){ public void run() { OneClass two=OneClass.getInstance(); two.func(); } }).start();; OneClass one=OneClass.getInstance(); one.func(); } }
当然也可以将锁移动到内部具体实例化的代码段,来缩小加锁区域,其中需要额外注意的是在获取到锁之后需要再次判断下instance引用是否为空(因为线程获得到锁之后会继续执行下去):
public static OneClass getInstance(){ if(instance==null){ synchronized(OneClass.class){ if(instance==null){ instance=new OneClass(); } } } return instance; }
最后给出一种最简洁的写法,通过内部类来获取单例,由于内部类在调用getInstance的时候才会被初始化,因此达到懒汉模式:
public class OneClassBest { private OneClassBest(){ } private static class ClassHandle{ private static OneClassBest oneInstace=new OneClassBest(); } public static OneClassBest getInstance(){ return ClassHandle.oneInstace; } }