Java基础知识学习-----单例设计模式

1、问题:如何在Java中创建线程安全的单例

要回答这样的问题,首先要了解什么是单例--一个类只有一个实例,其次在了解单例的几种模式

1、饿汉模式

public class Singleton{

public Singleton(){}

public static Singleton singleton = new Singleton();

public static Singleton getInstance(){
return singleton;
}
}

缺点:浪费资源空间

2、懒汉模式

public class Singleton{

public Singleton(){}

public static Singleton singleton;

public static Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}

优点:可以实现延迟加载,即什么时候使用什么时候加载实例

缺点:多线程下会造成会生成多实例

import java.util.concurrent.FutureTask;

public class Singleton {

    public static Singleton singleton;

    public static Singleton getInstance(){
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }

}

class Test1 {

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread th = new Thread() {
                @Override
                public void run() {
                    System.out.println(Singleton.getInstance());
                }
            };
            th.start();
        }
    }
}

执行结果,多线程情况下会出现多实例情况

多线程情况下,如何保证线程安全,加锁

public class Singleton {

    public static Singleton singleton;

    public static synchronized Singleton getInstance(){
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }

}

class Test1 {

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            Thread th = new Thread() {
                @Override
                public void run() {
                    System.out.println(Singleton.getInstance());
                }
            };
            th.start();
        }
    }
}

执行结果

通过添加类锁,可以防止锁多次实例化,缺点由于对整个getInstance方法加锁,性能较低,限定了访问速度

3、双重检测机制(DCL)-- 懒汉模式

public class Singleton {

    public static Singleton singleton;

    public static  Singleton getInstance(){
            synchronized (Singleton.class) {
                if(singleton==null) {
                    singleton = new Singleton();
                }
            }
        return singleton;
    }

}

执行结果:

结果再次证明生成了多实例,原因是因为线程A和线程B均在未初始化签获取对象,此时对象分别为null,最终实例化为新对象 

解决方案,在锁之前进行null判断,但是忽略了一点,singleton = new Singleton() 不是原子性的

加载一个对象的过程

(1)分配内存空间

(2)执行构造函数,初始化对象

(3)将对象指向内存空间

不同的服务器下,有可能造成指令重排,即(1)-> (3) -> (2)这时线程A持有未初始化对象A,线程B进入后对象已经不为空,直接使用对象,但是此对象未被初始化,容易出现问题,这是我们只需引入volatitle关键字


public class Singleton {

    public static volatile Singleton singleton;

    public static  Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

上面代码为双重检测锁(DCL)完整代码

4、静态内部类实现线程安全(推荐)

public class Singleton {

 public static class SingletonHolder{
     private static Singleton instance = new Singleton();
 }
 

 public static Singleton getInstance(){
     return SingletonHolder.instance;
 }

}

当Singleton被加载时,其内部类不会被初始化,所以实例也不会被初始化,只有getInstance被调用时,才会加载SingletonHolder从而instance才会被初始化,实例的建立是类加载时发生的,天生对线程友好,所以不用再使用volatitle

5、枚举单例模式

public class Singleton {

public Singleton(){}

 public enum SingletonEnum{

    INSTANCE;
    SingletonEnum(){
        singletonEnum = new Singleton();
    }

    private Singleton singletonEnum;

    public Singleton getInstance(){
        return singletonEnum;
    }
}

}


我们在访问枚举时,都会执行构造函数,由于枚举枚举的实例都是static final类型,所以对象只能被实例化一次

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值