网上到处都是懒汉,饿汉模式。给两个Demo就算过去了吧。
饿汉单例模式:在类加载的时候,就开始实例化了。
public class HungrySingleton {
private static HungrySingleton one=new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return one;
}
public static void main(String[] args) {
HungrySingleton one1=HungrySingleton.getInstance();
HungrySingleton one2=HungrySingleton.getInstance();
System.out.println(one1==one2);
}
}
懒汉模式:在第一次获取实例化对象的时候,开始实例化。
public class LazySingleton {
private static LazySingleton one=null;
private LazySingleton() {
}
public static LazySingleton getInstance(){
if(one==null){
one=new LazySingleton();
}
return one;
}
public static void main(String[] args) {
LazySingleton one1=LazySingleton.getInstance();
LazySingleton one2=LazySingleton.getInstance();
System.out.println(one1 == one2);
}
}
无论何种模式先要把构造函数给私有化,否则就变成了“勤快汉”模式了;这名字是我瞎编的。
饿汉模式典型用法:Spring中IOC容器ApplictionContext,连接池.
懒汉模式典型用法:不知道啊。
饿汉模式没缺点,最多也就在没使用的时候,分配个内存空间。
下面着重说说懒汉模式,所以来个分割线吧。
=================================
懒汉单例模式的线程安全
上面的懒汉模式有线程安全问题,就是多个线程在同时执行的时候,怎么保证LazySingleton只被实例化了一次。
线程类:
public class ExectorThread implements Runnable {
@Override
public void run() {
LazySimpleSingleton one=LazySimpleSingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + one);
}
}
单例类:
public class LazySimpleSingleton {
private static LazySimpleSingleton one=null;
private LazySimpleSingleton() {
}
public static LazySimpleSingleton getInstance(){
if(one==null){
one= new LazySimpleSingleton();
}
return one;
}
}
测试类:
public class LazyTest {
public static void main(String[] args) {
Thread t1=new Thread(new ExectorThread());
Thread t2=new Thread(new ExectorThread());
t1.start();
t2.start();
System.out.println("end");
}
}
第一个线程吧one实例化完成之后,还没有来得及刷新到内存,第二个线程就把one读入内存,又进行了一次实例化。
最简单的办法就是给实例化方法getInstance()添加一个synchronized.
修改后代码如下
public class LazySimpleSingleton {
private static LazySimpleSingleton one=null;
private LazySimpleSingleton() {
}
public static synchronized LazySimpleSingleton getInstance(){
if(one==null){
one= new LazySimpleSingleton();
}
return one;
}
}
这种模式有一个性能问题;比如100个线程在同时调用getInstance()的时候,99个全部都阻塞在这个位置了,
包括one已经不是空值的时候,依然在阻塞中;改造上面的代码,让已经实例化之后的线程不在阻塞。
1 public class LazySimpleSingleton {
2
3 private static LazySimpleSingleton one=null;
4
5 private LazySimpleSingleton() {
6 }
7 public static LazySimpleSingleton getInstance(){
8 //索前判断是否实例化了,实例化了就不用进入synchronized中了
9 if(one==null){
10 synchronized(LazySimpleSingleton.class){
11 //上面one==null了,不代表此时还是null
12 if(one==null){
13 one= new LazySimpleSingleton();
14 }
15 return one;
16 }
17 }
18 return one;
19 }
20 }
反射破坏单例
以饿汉单例的Demo为例子进行改造。
1 public class HungrySingleton {
2
3 private static HungrySingleton one=new HungrySingleton();
4
5 private HungrySingleton(){}
6
7 public static HungrySingleton getInstance(){
8 return one;
9 }
10
11 public static void main(String[] args) throws Exception{
12 HungrySingleton one1=HungrySingleton.getInstance();
13 Constructor constructor=HungrySingleton.class.getDeclaredConstructor(null);
14 //强制访问构造器,包括私有成员
15 constructor.setAccessible(true);
16 HungrySingleton one2=(HungrySingleton) constructor.newInstance();
17 System.out.println(one1==one2);
18 }
19 }
打印结果显示false.说明被实例化了两次;修改代码如下。
public class HungrySingleton {
private static HungrySingleton one=new HungrySingleton();
private HungrySingleton(){
if(one!=null){
throw new RuntimeException("已经实例化过了,本次实例化失败");
}
}
public static HungrySingleton getInstance(){
return one;
}
public static void main(String[] args) throws Exception{
HungrySingleton one1=HungrySingleton.getInstance();
Constructor constructor=HungrySingleton.class.getDeclaredConstructor(null);
//强制访问构造器,包括私有成员
constructor.setAccessible(true);
HungrySingleton one2=(HungrySingleton) constructor.newInstance();
System.out.println(one1==one2);
}
}
打印结果:
序列化破坏单例模式
以饿汉模式为例:
public class SeriableSingleton implements Serializable {
public final static SeriableSingleton one=new SeriableSingleton();
private SeriableSingleton() {
}
public static SeriableSingleton getInstance(){
return one;
}
}
测试类:
1 public class SeriableSingletonTest {
2 public static void main(String[] args) {
3 SeriableSingleton s1=null;
4 SeriableSingleton s2=SeriableSingleton.getInstance();
5
6 FileOutputStream fos=null;
7 try {
8 fos=new FileOutputStream("one.obj");
9 ObjectOutputStream oos=new ObjectOutputStream(fos);
10 oos.writeObject(s2);
11 oos.flush();
12 oos.close();
13
14 FileInputStream fis=new FileInputStream("one.obj");
15 ObjectInputStream ois=new ObjectInputStream(fis);
16 s1=(SeriableSingleton) ois.readObject();
17 ois.close();
18
19 System.out.println(s1 == s2);
20
21 } catch (Exception e){
22
23 }
24 }
25 }
显示结果是false.
这个问题很好办,加一行代码的事情。
1 public class SeriableSingleton implements Serializable {
2 public final static SeriableSingleton one=new SeriableSingleton();
3
4 private SeriableSingleton() {
5 }
6
7 public static SeriableSingleton getInstance(){
8 return one;
9 }
10
11 private Object readResolve(){
12 return one;
13 }
14 }
上面红色就是添加的一个方法。
因为JDK在readObject()时候,判断了有没有readResolve()方法,如果有的话就执行这个方法,没有就不执行了,我们充分利用了这个特点,给他直接返回了一个one对象;
所以就不执行实例化了。