单例模式适用场景
- 无状态的工具类:比如日志工具类,不管是在哪里使用,我们需要的只是它帮我们记录日志信息,除此之外,并不需要在它的实例对象上存储任何状态,这时候我们只需要一个实例对象即可。
- 全局信息类:比如我们在一个类上记录网站的访问次数,我们不希望有的访问被记录在对象A上,有的却记录在对象B上,这时候我们就希望这个类成为单例。
饿汉式
第一种实现方式
package singleton;
/**
* 描述: 饿汉式(静态常量)[可用,线程安全]
* 从代码中我们看到,类的构造函数定义为private的,保证其他类不能实例化此类,然后提供了一个静态实例并返回给调用者。
* 其实现简单,并且在类加载的时候就对实例进行创建,实例在整个程序周期都存在。
*
* 优点: 只在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。
* 缺点: 即使这个单例没有用到也会被创建,而且在类加载之后就被创建,这样一来就存在着内存浪费。
* 适用于:这种实现方式适合占用内存比较小的单例对象,且在初始化时就会被用到的情况。但是,如果单例占用的内存比较 大,或单例只是在某个特定场景下才会用到,使用饿汉模式就不合适了,这时候就需要用到懒汉模式进行延迟加载。
*/
public class Singleton1 {
private final static Singleton1 instance = new Singleton1();
private Singleton1(){
}
public static Singleton1 getInstance(){
return instance;
}
}
第二种实现方式
/**
* 描述: 饿汉式(静态代码块)(可用,与上个案例实现原理基本相似,也是线程安全)
*
*/
public class Singleton2 {
private final static Singleton2 INSTANCE;
static {
INSTANCE = new Singleton2();
}
private Singleton2() {
}
public static Singleton2 getInstance() {
return INSTANCE;
}
}
懒汉式
第一种实现方式
package singleton;
/**
* 描述: 懒汉式(线程不安全)
*
* 优点:懒汉模式中单例是在需要的时候才去创建的,如果单例已经创建,再次调用获取接口将不会重新创建新的对象,而是直接返回之前创建的对象。
* 缺点:但是这里的懒汉模式并没有考虑线程安全问题,在多个线程可能会并发调用它的getInstance()方法,导致创建多个实例,因此需要加锁解决线程同步问题
* 适用于:如果某个单例使用的次数少,并且创建单例消耗的资源较多,那么就需要实现单例的按需创建,这个时候使用懒汉模式就是一个不错的选择。
*/
public class Singleton3 {
private static Singleton3 instance;
private Singleton3() {
}
// 在获取类对象的时候进行初始化,
// 但是这里的懒汉模式并没有考虑线程安全问题,
// 在多线程环境下可能会并发调用它的getInstance()方法,导致创建多个实例
public static Singleton3 getInstance() {
// 如果多线程并行if (instance == null)这行代码,则会多次创建instance
if (instance == null) {
instance = new Singleton3();
}
return instance;
}
}
第二种实现方式
package singleton;
/**
* 描述: 懒汉式(线程安全)(不推荐)
*/
public class Singleton4 {
private static Singleton4 instance;
private Singleton4() {
}
// 由于synchronized关键字的使用会造成该方法效率极低,因此不推荐使用
public synchronized static Singleton4 getInstance() {
if (instance == null) {
instance = new Singleton4();
}
return instance;
}
}
第三种实现方式
package singleton;
/**
* 描述: 懒汉式(线程不安全)(不推荐)
*/
public class Singleton5 {
private static Singleton5 instance;
private Singleton5() {
}
// 事实上,该方法依旧是线程不安全。why?原因是当多个线程走到 "if (instance == null)"这里后,
// 会造成线程阻塞。但第一个线程运行结束后,释放锁后,第二个线程依旧会进入该方法,依旧会创建新的对象。
// 所以即使加入synchronized方法,线程依旧不安全。
public static Singleton5 getInstance() {
if (instance == null) {
synchronized (Singleton5.class) {
instance = new Singleton5();
}
}
return instance;
}
}
第四种实现方式
package singleton;
/**
* 描述: 双重检查(推荐使用)
*/
public class Singleton6 {
/**
* 之所以使用volatile关键字是为了阻止重排序,以及可见性,原因是创建对象并非原子操作。
* 因为创建对象包含3个步骤:1. 新建一个空的Person对象。2. 把这个对象的地址指向引用。
* 3.执行Person的构造函数。
*/
private volatile static Singleton6 instance;
private Singleton6() {
}
/**
* 该方法线程安全。why?原因是当多个线程走到 "if (instance == null)"这里后,
* 会造成线程阻塞。但第一个线程运行结束后,释放锁后,第二个线程依旧会进入该方法,
* 一旦线程走到synchronized代码段后都会再次判断实例是否创建,所以线程安全。
*/
public static Singleton6 getInstance() {
if (instance == null) {
synchronized (Singleton6.class) {
// 一旦线程走到了都会判断实例是否创建
if (instance == null) {
instance = new Singleton6();
}
}
}
return instance;
}
}
网友评论