单例模式
单例模式即创建的对象具有唯一性,通过方法获取的都是同一个对象。(将对象的创建和获取分离)
实现单例模式的方法主要分为:
饿汉式:类加载阶段创建对象。
懒汉式:使用时创建对象。
下面是饿汉式创建的几种方式:
1.静态成员变量方式
class Dog{
private Dog(){}
private static final Dog dog =new Dog();
public static Dog dog(){
return dog;
}
}
2.静态代码块方式
class Dog4{
private static Dog4 dog4;
private Dog4(){}
static {
dog4=new Dog4();
}
public static Dog4 dog4(){
return dog4;
}
}
3.枚举方式(无线程安全、序列化和反射破坏单例的问题)
enum Dog1{
DOG_1;
}
下面是懒汉式创建的几种方式:
1.线程安全的实现方式
class Dog2 {
private static Dog2 dog2 = null;
private Dog2() {}
public static Dog2 dog2() {
synchronized (Dog2.class) {
if (dog2 == null) {
return dog2 = new Dog2();
}
}
return dog2;
}
}
2.dcl方式
详解dcl问题:
为什么需要利用双重检查?
因为线程安全的单例实现方式,会造成在第1次并发至第n次并发中,线程都会在synchronized进行阻塞等待,影响性能;而dcl的方式可以使得只需要在第1次并发时在synchronized进行阻塞等待,后续次数的并发无需再进行阻塞等待,提高了系统性能。
为什么需要在单例对象上添加volatile关键字?
从dog3=new Dog3()进行分析,对象的创建总共分为6个步骤:1.检查类是否加载到方法区,未加载进行类加载;2.在堆内存中分配内存空间,有两种分配方式,分为指针碰撞(标记-压缩算法)和空闲列表(标记-清除算法)的方式;3.处理并发问题(每个线程分配一个TLAB)4.默认初始化对象信息;5.对象头信息设置;6.执行init方法,显示初始化;7.赋值引用变量。在上述中,6和7的步骤是划分开的,在程序执行过程中可能会发生指令重排序,即赋值操作会先于初始化对象信息执行。
在dcl中,多线程环境下,由于上述的指令重排序问题可能会导致程序运行出错。若A线程获取到synchronized的锁,那么编译器优化导致dog3=new Dog3()重排序,赋值指令先于初始化对象指令执行。在A线程完成赋值操作时,B线程在进行第一个if判断,由于A线程已经对变量进行了赋值,因此,B线程直接返回了该引用变量(该变量所指对象是一个“空壳”对象),在对象初始化任务量繁杂的情况下,将造成B线程接下来使用该变量时出错。
class Dog3{
private static volatile Dog3 dog3=null;
private Dog3(){}
public static Dog3 dog3(){
if(dog3==null){
synchronized (Dog3.class){
if(dog3==null){
return dog3=new Dog3();
}
}
}
return dog3;
}
}
3.静态内部类方式
class Dog5{
private Dog5(){}
private static class DogDemo{
private static final Dog5 dog5=new Dog5();
}
public static Dog5 dog5(){
return DogDemo.dog5;
}
}
解决序列化和反射破坏单例的问题(以静态内部类实现方式为例)
1.实现readResolve方法解决序列化破坏单例的问题
2.使用构造器抛异常的方式解决反射破坏单例的问题
class Dog5{
//解决反射破坏单例
private boolean flag=false;
private Dog5(){
synchronized (Dog5.class){
if(flag){
throw new RuntimeException("不能创建多个对象");
}
flag=true;
}
}
private static class DogDemo{
private static final Dog5 dog5=new Dog5();
}
public static Dog5 dog5(){
return DogDemo.dog5;
}
//解决序列化破坏单例问题
public Object readResolve(){
return DogDemo.dog5;
}
}