单例模式是常听说的一种设计模式,它的用法是保证在一个类在任何情况下都只有一个对象,我们常见的有springIOC的applicationContext,数据库的连接池等。
单例模式分为两种:饿汉式和懒汉式。
我们先说饿汉式,饿汉式如其名字一样,像一个饿汉要先创建出一个对象来,来看代码
public class HungurySingle {
public static HungurySingle hungurySingle = new HungurySingle();
private HungurySingle(){
}
public static HungurySingle getInstance(){
return hungurySingle;
}
}
//这是测试类
public class SingleTest {
public static void main(String[] args) {
HungurySingle hungurySingle = HungurySingle.getInstance();
System.out.println(hungurySingle);
new Thread(() -> {
HungurySingle hungurySingle12 = HungurySingle.getInstance();
System.out.println(hungurySingle12);
}).start();
new Thread(() -> {
HungurySingle hungurySingle1 = HungurySingle.getInstance();
System.out.println(hungurySingle1);
}).start();
}
}
运行结果:
com.demo.wqx.partten.single.HungurySingle@36baf30c
com.demo.wqx.partten.single.HungurySingle@36baf30c
com.demo.wqx.partten.single.HungurySingle@36baf30c
代码不难理解,从运行结果来看多线程的情况下也是只有一个对象的。饿汉式的缺点就是不用这个对象也会创建一个对象,浪费空间,接下来来看懒汉式,懒汉式就要稍微复杂一点了,也是像名字一样,这个对象很懒,不会提前把对象创建出来,要需要他的时候才会创建。来看代码
public class LazySingle {
public static LazySingle lazySingle;
private LazySingle(){
}
/**
* 对象为空则new一个,否则直接返回。
* @return
*/
public static LazySingle getInstance(){
if(lazySingle == null){
lazySingle = new LazySingle() ;
}
return lazySingle;
}
}
一个简单的懒汉式单例,就出来了,这里没有写测试类,读者可以思考一下是否有问题,接下来我们来测试一下。
//测试类
public class LazyTest {
public static void main(String[] args) {
for (int i = 0; i < 5; i++)
new Thread(()->{
LazySingle lazySingle = LazySingle.getInstance();
System.out.println(lazySingle);
}).start();
}
}
运行结果:
com.demo.wqx.partten.single.LazySingle@688ee48d
com.demo.wqx.partten.single.LazySingle@685c0ab0
com.demo.wqx.partten.single.LazySingle@685c0ab0
com.demo.wqx.partten.single.LazySingle@688ee48d
com.demo.wqx.partten.single.LazySingle@688ee48d
看到这里,应该发现问题了吧,上面的代码是线程不安全的,如果有两个线程同时调用这个方法的话,就会new多个对象。解决办法也好说,synchronized关键字可以解决。来看这段代码
public static synchronized LazySingle getInstance(){
if(lazySingle == null){
lazySingle = new LazySingle() ;
}
return lazySingle;
}
运行结果
com.demo.wqx.partten.single.LazySingle@688ee48d
com.demo.wqx.partten.single.LazySingle@688ee48d
com.demo.wqx.partten.single.LazySingle@688ee48d
com.demo.wqx.partten.single.LazySingle@688ee48d
com.demo.wqx.partten.single.LazySingle@688ee48d
可以看到解决了线程安全的问题,但是我们只是5个线程,如果是500个 5000个线程呢,那么后面的会阻塞很久,显然效率太低了,然后我们再想想优化的方法。这样的话会好很多吧,也就阻塞一次new的时间了。
public static LazySingle getInstance(){
if(lazySingle == null){
synchronized (LazySingle.class){
if(lazySingle == null) {
lazySingle = new LazySingle();
}
}
}
return lazySingle;
}
上面基本上就是我们常用的单例模式了,但是synchronized 总归是会浪费性能的,还有其他的方法优化吗?考虑一下内部类
public class LazySingle_two {
private LazySingle_two(){
}
public static final LazySingle_two getInstance(){
return LazyInnerClassSingleton.lazySingle_two;
}
/**
* 内部类不初始化的话,不会创建外部类的对象
*/
private static class LazyInnerClassSingleton {
private static final LazySingle_two lazySingle_two = new LazySingle_two();
}
}
这个留给读者自己测试一下吧,我测试是没有问题的。运行结果就不写出来了。这里貌似没有什么可以优化的了,懒汉式我们也介绍完了,但是单例还有一个问题就是 反射创建对象,上面单例模式的实现方式都是构造方法私有化,但是反射可以强制创建对象。我们再来优化一下刚才通过内部类创建单例对象的方法。
public class LazySingle_two {
private LazySingle_two(){
if(LazyInnerClassSingleton.lazySingle_two != null){
throw new RuntimeException("不允许创建不同单例的对象");
}
}
public static final LazySingle_two getInstance(){
return LazyInnerClassSingleton.lazySingle_two;
}
/**
* 内部类不初始化的话,不会创建外部类的对象
*/
private static class LazyInnerClassSingleton {
private static final LazySingle_two lazySingle_two = new LazySingle_two();
}
}
ok 这样就完美解决反射创建对象的问题。