简介
所谓的单例设计模式,就是采取一定的办法保证整个系统中,对某个类只存在一个对象实例,并且该类只提供一个获取该对象实例的方法,同时这个方法是静态的。 使用场景: 需要频繁的进行创建和销毁的对象、创建对象耗时很多,但是经常使用的对象、频繁访问数据库或文件的对象。
七种实现方式
第一种---饿汉式(静态常量)现方式
所谓饿汉式,通俗来讲就是我饿了,你得给我准备好,我想吃了就直接拿着吃了,别现做~ 过程如下
- 1、构造器私有化(防止new创建)
- 2、在类的内部创建对象
- 3、向外暴露一个静态的公共方法 下面是代码实现
java
public class StaticConstant {
/**
* 构造方法私有化
*/
private StaticConstant(){}
/**
* 静态常量实例对象
*/
private static final StaticConstant staticConstant = new StaticConstant();
/**
* 对外暴露的静态方法
* @return
*/
public static StaticConstant getInstance(){
return staticConstant;
}
}
优点
写法简单,类装载的时候就完成了实例化,没有线程安全问题。
缺点
在类装载的时候就完成了实例化,没有达到懒加载的效果,如果没有使用过这个实例,其实就是一种内存浪费。
第二种---饿汉式(静态代码块)实现方式
- 1、构造器私有化(防止new创建)
- 2、在类的静态代码块中创建实例对象
- 3、向外暴露一个静态的公共方法
java
public class StaticCodeBlock {
/**
* 构造方法私有化
*/
private StaticCodeBlock(){}
/**
* 静态常量实例对象
*/
private static StaticCodeBlock staticCodeBlock;
/**
* 静态代码块
*/
static {
staticCodeBlock = new StaticCodeBlock();
}
/**
* 对外暴露的静态方法
* @return
*/
public static StaticCodeBlock getInstance(){
return staticCodeBlock;
}
}
优点和缺点和第一种方式一样!!
第三种---线程不安全的懒汉式实现方式
- 1、构造器私有化(防止new创建)
- 2、向外暴露一个静态的公共方法
- 3、在公共方法是完成实例对象的创建,但是需要判断是否已经创建过 代码如下
java
public class ThreadNoSafe {
/**
* 构造方法私有化
*/
private ThreadNoSafe(){}
/**
* 静态常量实例对象
*/
private static ThreadNoSafe threadNoSafe;
/**
* 对外暴露的静态方法
* @return
*/
public static ThreadNoSafe getInstance(){
if(threadNoSafe == null){
threadNoSafe = new ThreadNoSafe();
}
return threadNoSafe;
}
}
优点
起到了懒加载的效果
缺点
只能在单线程的情况下使用,我们可以看到在if(threadNoSafe == null)这行语句中,多线程的环境下,一个线程完成了这个判断,但是还没有执行向下的代码,另一个线程也进来了,也执行判断,判断的结果则也是null,所以就会存在多个线程同时执行创建的情况,那么就会产生多个实例对象,所以实际开发中不要使用这种方式。
第四种---线程安全的懒汉式实现方式
步骤同第三种,但是在getInstance方法上加上sychronized 代码如下
java
public class ThreadSafe {
/**
* 构造方法私有化
*/
private ThreadSafe(){}
/**
* 静态常量实例对象
*/
private static ThreadSafe threadSafe;
/**
* 对外暴露的静态方法
* @return
*/
public static synchronized ThreadSafe getInstance(){
if(threadSafe == null){
threadSafe = new ThreadSafe();
}
return threadSafe;
}
}
优点
解决了线程不安全的问题。
缺点
效率太低了,每个线程想要获取类的实例的时候都需要同步。
第五种---线程安全的懒汉式(双重检查)实现方式
过程同四类似,但是sychronized换了位置,代码如下
java
public class ThreadSafeDoubleCheck {
/**
* 构造方法私有化
*/
private ThreadSafeDoubleCheck(){}
/**
* 静态常量实例对象
*/
private static ThreadSafeDoubleCheck threadSafeDoubleCheck;
/**
* 对外暴露的静态方法
* @return
*/
public static synchronized ThreadSafeDoubleCheck getInstance(){
if(threadSafeDoubleCheck == null){
synchronized (ThreadSafeDoubleCheck.class) {
if(threadSafeDoubleCheck == null) {
threadSafeDoubleCheck = new ThreadSafeDoubleCheck();
}
}
}
return threadSafeDoubleCheck;
}
}
这个是推荐使用的一种方式,我们可以看到这是一个双重检查,尽管第一次判断有多个线程进入,那么进入第二个判断的也只有一个线程,这个线程创建完之后,其他线程进入判断的时候,实例对象就不是空的了,所以是双重检查的单例对象的创建方式是线程安全的,也是推荐使用的。
第六种---静态内部类的实现方式(线程安全)
在类加载时候内部类是不会加载的,而是当调用到内部类的时候才会去加载,同时类加载的时候是线程安全的
- 1、构造器私有化(防止new创建)
- 2、在类的内部编写一个内部类,内部类中创建一个当前类的常量对象。
- 3、向外暴露一个静态的公共方法,返回内部类中的常量对象实例。 代码如下
java
public class InnerClass {
/**
* 构造方法私有化
*/
private InnerClass(){}
/**
* 内部类
*/
private static class SingletonClass{
private static final InnerClass instance = new InnerClass();
}
/***
* 对外暴露的static方法
* @return
*/
public static InnerClass getInstance(){
return SingletonClass.instance;
}
}
JVM帮我们保证了线程安全,同时利用静态内部类的特点实现了延迟加载,推荐使用。
第七种--- 枚举方式实现(线程安全)
借助枚举实现单例,不仅能避免多线程的问题,还能方式反序列化创建新的对象。代码如下
java
public enum EnumSingleton {
INSTANCE;
public void test(){
}
}
推荐使用