设计模式_单例模式(singleton)

设计模式_单例模式(singleton)

单例模式:只能创建一个实例,针对开发业务中一个实例就够了。这种情况下建议用单例模式。
如:工具类,资源类。

1. 单例模式写法
单例模式有懒汉、饿汉、枚举、静态内部类、双重校验锁。
这里我只写常用的两种:饿汉、静态内部类。附带的会与一些别的比较但是肯定不会写全的。
饿汉
这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大多数都是调用getInstance方法,这时候初始化instance显然没有达到lazy loading(延迟加载)的效果。这种适合于类开始就需要被装载的情况

public class Car {
    private static final Car car = new Car();

    private Car(){}

    public static  Car getInstance(){
        return car;
    }

    public void driveCar(int index){
        System.out.println("驾驶员已经启动"+index+"汽车");
    }
}

与之相比较的话可以看静态内部类的写法
静态内部类
这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟饿汉方式不同的是(很细微的差别):饿汉方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比饿汉方式就显得很合理。

public class CarStaticClass {
    private static class CarStaticClassHolder{
        private static CarStaticClass car = new CarStaticClass();
    }
    private CarStaticClass(){};

    public static final CarStaticClass getInstance(){
        return CarStaticClassHolder.car;
    }
}

以上两种是最常用的写法。这里在写一种懒汉的方式,以及懒汉方式要注意的一点。
懒汉(线程不安全)
这种方式一般情况下,多线程同时调用的时候会创建多个对象。显然就达不到了单例的目的。因为在一个线程(A)调用的时候Car为空。另一个线程(B)调用的时候Car也为空。那么就会同时创建两个Car对象。也就是返回两个Car对象。所以这种写法一般是达不到Singleton的目的。但是很明显的是lazy loading,但是由于线程不安全只能pass掉。

/*实例化类*/
public class Car {
    private static Car car;

    private Car(){}

    public static synchronized Car getInstance(){
        if(car == null){
            System.out.println("创建Car对象");
            car = new Car();
        }
        return car;
    }

    public void driveCar(int index){
        System.out.println("驾驶员已经启动汽车");
    }
}

/*========================================*/
/*调用类这里可以看出多线程的方式调用。代码都比较简单就没写注释了*/
public class Client extends Thread{

    public void run(){
        for(int i =0 ;i<5; i++){
            Car car = Car.getInstance();
            car.driveCar(i);
        }
    }


    public static void main(String args[]){
        Client client1 = new Client();
        Client client2 = new Client();
        client1.start();
        client2.start();
    }
}

/*========================================*/
/*运行结果*/
创建了Car
创建了Car
驾驶员已经启动0汽车
驾驶员已经启动0汽车
驾驶员已经启动1汽车
驾驶员已经启动2汽车
驾驶员已经启动3汽车
驾驶员已经启动4汽车
驾驶员已经启动1汽车
驾驶员已经启动2汽车
驾驶员已经启动3汽车
驾驶员已经启动4汽车

懒汉(线程安全)
与线程不安全相比,代码改动很少,加了一个锁。也是有lazy loading的效果。但是告诉你的是这种方式效率很低。

public class Car {
    private static final Car car = new Car();

    private Car(){}

    public static synchronized  Car getInstance(){
        return car;
    }

    public void driveCar(int index){
        System.out.println("驾驶员已经启动"+index+"汽车");
    }
}

至于其他的几种方式。实际工作中好像很少看到。这里只是做一些简单的说明。枚举方式比较少见我觉得他的主要作用能防止反序列化重新创建新的对象。如果有实例化需求的话可以实现序列化的接口。
双重校验锁的方式我是觉得没必要。如果不是特定的场合实在没有必要用双重校验锁的方式。详细请看
2. 说明
单例模式为什么能够成为单例,可能代码有正常的区别也不大。认真的小伙伴们,你们看单例模式的 构造方法 他的构造方式是无参、私有的。这样做就能做到外部不能通过new的方式创建对象。就只能通过getInstance方法来构造。而在这个方法里面是返回的对象,这个对象已经初始化完成了。而且是final的static的。这种方式下不会有新的对象产生的。
至于是用饿汉还是静态内部类的方式就看场景了。一般都是用饿汉,除非明确使用静态内部类的才会用。用的时候一定保证线程安全。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值