所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session 对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个 SessionFactory就够,这是就会使用到单例模式。
优缺点:实现简单,但是没有达到lazy loading效果。可用但可能造成内存浪费。
package singleton;
/**
* @Author Worm
* @Date 2020/8/9 16:57
* @Version 1.0
**/
public class SingletonTest01 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);//true
System.out.println(instance.hashCode());/*42121758*/
System.out.println(instance1.hashCode());/*42121758*/
}
}
//饿汉式(静态常量)
class Singleton {
// 1.构造器私有化
private Singleton() {
}
//2.本类内部创建对象实例
private final static Singleton instance = new Singleton();
//提供一个公共静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
和上一种一样
package singleton;
/**
* @Author Worm
* @Date 2020/8/9 17:09
* @Version 1.0
**/
public class SingletonTest02 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);//true
System.out.println(instance.hashCode());/*42121758*/
System.out.println(instance1.hashCode());/*42121758*/
}
}
//饿汉式(静态代码块)
class Singleton1 {
// 1.构造器私有化
private Singleton1() {
}
//2.本类内部创建对象实例
private final static Singleton1 instance;/*final修饰的属性必须显式赋值*/
static {
instance = new Singleton1();
}
//提供一个公共静态方法,返回实例对象
public static Singleton1 getInstance() {
return instance;
}
}
虽然起到了lazy loading的效果。但是只能在单线程下使用,一但多个线程同时进入if条件,便可能产生多个实例。实际开发中不能使用这种方式。
package singleton.type2;
/**
* @Author Worm
* @Date 2020/8/9 17:16
* @Version 1.0
**/
public class SingletonTest03 {
public static void main(String[] args) {
System.out.println("懒汉式1,线程不安全~,实际开发不使用");
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);//true
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
虽然解决了线程不安全问题,但是效率太低。因为线程每次访问方法时都会同步,但实际只需要第一次保证同步就可以,之后的同步是毫无意义的。
package singleton.type3;
/**
* @Author Worm
* @Date 2020/8/9 17:16
* @Version 1.0
**/
public class SingletonTest04 {
public static void main(String[] args) {
System.out.println("懒汉式2,线程安全~,同步方法");
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);//true
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {
}
//加入同步处理的代码,解决线程安全问题。但每个线程每次都要同步,实际上这个方法只需执行一次实例化代码,后面都是直接return的。这样效率太低
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种方法本想改进第四种,提高效率。但实际上本方法不能起到线程同步作用。
Double-Check概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。
实际开发中推荐使用,既延迟加载,而且效率较高。
package singleton.type4;
/**
* @Author Worm
* @Date 2020/8/9 17:16
* @Version 1.0
**/
public class SingletonTest05 {
public static void main(String[] args) {
System.out.println("懒汉式,双重同步");
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);//true
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}
class Singleton {
// volatile:使变量一有修改立马提交到主存里面去
private static volatile Singleton instance;
private Singleton() {
}
//加入双重检查代码,解决线程安全问题,同时解决懒加载问题
public static synchronized Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
本方式使用类装载机制来保证初始化实例时只有一个线程。注意,内部类不会在主类装载时立即实例化,而是在调用getInstance方法,时才会装载SingletonInstance类,从而完成Singleton的实例化。 同样推荐使用。
package singleton.type5;
/**
* @Author Worm
* @Date 2020/8/9 17:16
* @Version 1.0
**/
public class SingletonTest06 {
public static void main(String[] args) {
System.out.println("懒汉式,静态内部类单例模式,推荐使用");
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);//true
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {
}
// 静态内部类,该类中有一个静态属性Singleton
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
//直接返回SingletonInstance.INSTANCE
// 注意:上面的静态内部类不会随着主类加载就加载,而是会在下面的方法执行时才加载。
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
这种方式是Effective Java作者Josh Bloch 提倡的方式。不仅能避免多线程同步问题,而 且还能防止反序列化重新创建新的对象。
package singleton.type6;
import java.sql.SQLOutput;
/**
* @Author Worm
* @Date 2020/8/9 20:09
* @Version 1.0
**/
public class SingletonTest08 {
public static void main(String[] args) {
Singleton singleton = Singleton.INSTANCE;
Singleton singleton1 = Singleton.INSTANCE;
System.out.println(singleton == singleton1);/*true*/
System.out.println(singleton.hashCode());
System.out.println(singleton1.hashCode());
}
}
//枚举,推荐使用
enum Singleton {
INSTANCE;/*属性*/
public void sayOK() {
System.out.println("ok");
}
}
单例模式保证系统内存中只存在一个对象,当想实例化一个单例类时,使用相应的获取对象方法而不是new。单例模式的使用场景:需要频繁的进行创建和销毁的对象,创建对象时消耗的资源过多(即重量级对象)但又需经常用到的对象,工具类对象,频繁访问数据库的对象(诸如数据源,session工厂等)。