目录树如图:
写在最前:
如下五个文件分别对应:
1.(懒汉式)最经典的单例模式(多线程时有概率会出现问题)
2.(懒汉式、饱汉式)使用同步关键字进行改进,就是将getXXX方法进行同步化,
这样就可以防止多线程引发的问题了,可是执行效率低
3.(饿汉式)直接初始化单例对象 private static Singleton uniqueInstance= new Singleton();
这样在多线程的情况下也能保证只初始化一次
4.(双重检测)双重判断获取单例,先将其赋予null,在访问的时候在进行赋值,而且只进行一次,提高了效率
5.(静态内部类实现)
总结:
一般采用饿汉式(3),若对资源十分在意可以采用静态内部类(5),不建议采用懒汉式(1、2)及双重检测(4)
A01_原始懒汉式( 线程不安全 )
/**
* 经典单例模式,类的实例只有一个
* 介绍:单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。
* 通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
* 适用情况:比如java中的线程池,数据库连接池,和一些硬件设备,比如打印机等,这些资源的数量是一定的,
* 所以设计的时候就可以适用单例模式的思想。
*
* 如下是 懒汉式 的范例,但是其有线程安全问题,所以使用双重判断,也就是如下的A04___方式
*
*/
public class A01_Singleton {
//1.定义一个静态变量存储当前类的对象
private static A01_Singleton uniqueInstance= null;
//2.私有化构造方法
private A01_Singleton() {}
//3.定义一个静态方法返回类的对象
public static A01_Singleton getInstance() {
if(uniqueInstance==null) {
uniqueInstance = new A01_Singleton();
}
return uniqueInstance;
}
}
/**
*
* 经典单例模式分析:如果是在单线程的情况下经典单例模式没有什么问题,实现也比较简单,
* 但在多线程的情况下会出现问题,比如有两个线程A和B,线程是通过时间片来切换的,
* 一种极端的情况下,线程A和B都执行了if(uniqueInstance==null) 发现为空,
* 然后各自创建了一个对象,这样就会出现问题.
*
*/
A02_使用同步的懒汉式 ( 线程安全的懒汉式 )
/**
* 经典单例模式,类的实例只有一个
*
* 经典单例模式分析:如果是在单线程的情况下经典单例模式没有什么问题,实现也比较简单,
* 但在多线程的情况下会出现问题,比如有两个线程A和B,线程是通过时间片来切换的,
* 一种极端的情况下,线程A和B都执行了if(uniqueInstance==null) 发现为空,
* 然后各自创建了一个对象,这样就会出现问题.
*
* 改进一:使用同步关键字 (懒汉式、饱汉式)
*
*/
public class A02_Singleton {
private static A02_Singleton uniqueInstance= null;
private A02_Singleton() {}
//改进:使用synchronized关键字来保证单例.
public static synchronized A02_Singleton getInstance() {
if(uniqueInstance==null) {
uniqueInstance = new A02_Singleton();
}
return uniqueInstance;
}
}
/**
* 使用同步关键字来保证单例的话会消耗很多资源,如果需要获取单例对象的操作很少,
* 这种方法也可以使用,但当获取单例对象的操作很多时这样会极大的降低运行效率。
*
* 优点:资源利用率高,不执行getInstance()就不会被实例,可以执行该类的其他静态方法
*
* 缺点:第一次加载时不够快,多线程使用不必要的同步开销大
*/
A03_饿汉式【推荐的方式】
/**
* 经典单例模式,类的实例只有一个
*
* 使用同步关键字来保证单例的话会消耗很多资源,如果需要获取单例对象的操作很少,这种方法也可以使用,
* 但当获取单例对象的操作很多时这样会极大的降低运行效率。
*
* 改进二:直接初始化单例对象(也就是 饿汉式 的创建方式,直接初始化单例对象)【推荐使用该创建方式】
*/
public class A03_Singleton {
//直接初始化单例,这样在多线程的情况下也能保证只初始化一次
private static A03_Singleton uniqueInstance= new A03_Singleton();
private A03_Singleton() {}
public static A03_Singleton getInstance() {
return uniqueInstance;
}
}
/**
*
* 优点 1.线程安全
* 2.在类加载的同时已经创建好一个静态对象,调用时反应速度快
*
* 缺点 资源效率不高,可能getInstance()永远不会执行到,但执行该类的其他静态方法或者加载了该类(class.forName),
* 那么这个实例仍然初始化
*
*/
A04_双重判断获取单例
/**
* 经典单例模式,类的实例只有一个
*
* 改进三:双重判断获取单例
*/
public class A04_Singleton {
private static volatile A04_Singleton uniqueInstance= null;
private A04_Singleton() {}
//使用双重判断来保证单例并保证效率
public static A04_Singleton getInstance() {
if(uniqueInstance==null) {
synchronized (A04_Singleton.class) {
if(uniqueInstance==null) {
uniqueInstance = new A04_Singleton();
}
}
}
return uniqueInstance;
}
}
/**
* 这种方法存储单例对象的属性要设置成volatile来保证可见性,然后先判断一次,之后是一个类锁,
* 类锁会保证线程安全,在类锁中再判断一次,这样之后再第一次判断的时候初始化一次,
* 之后不会再执行类锁中的代码,所以提高了效率
*
* 优点:资源利用率高,不执行getInstance()就不被实例,可以执行该类其他静态方法
*
* 缺点:第一次加载时反应不快,由于java内存模型一些原因偶尔失败
*/
A05_静态内部类实现单例【 次推荐 】
/**
* 静态内部类实现
*/
public class A05_Singleton {
private A05_Singleton() {}
private static class SingletonHelp {
static A05_Singleton instance = new A05_Singleton();
}
public static A05_Singleton getInstance() {
return SingletonHelp.instance;
}
}
/**
* 优点:资源利用率高,不执行getInstance()不被实例,可以执行该类其他静态方法
*
* 缺点:第一次加载时反应不够快
*/
写在最后( 同写在最前 )
如下五个文件分别对应:
1.(懒汉式)最经典的单例模式(多线程时有概率会出现问题)
2.(懒汉式、饱汉式)使用同步关键字进行改进,就是将getXXX方法进行同步化,
这样就可以防止多线程引发的问题了,可是执行效率低
3.(饿汉式)直接初始化单例对象 private static Singleton uniqueInstance= new Singleton();
这样在多线程的情况下也能保证只初始化一次
4.(双重检测)双重判断获取单例,先将其赋予null,在访问的时候在进行赋值,而且只进行一次,提高了效率
5. 静态内部类实现
总结:
一般采用饿汉式(3),若对资源十分在意可以采用静态内部类(5),不建议采用懒汉式(1、2)及双重检测(4)