饿汉式
package day0518;
/**
* @author liw
* @date 2020-07-01
*/
public final class Singleton {
//实例变量
private byte [] data = new byte[1024] ;
//在定义实例变量的时候直接初始化
private static Singleton instance = new Singleton();
//私有构造
private Singleton() {
}
public static Singleton getInstance(){
return instance ;
}
}
饿汉式的关键在于instance 作为类变量并且直接得到了初始化,如果住的的使用了Singleton类难么instance实例将直接完成创建,包括其中的实例变量都会得到初始化,1k的空间的data会被同时创建。
懒汉式
package day0518;
/**
* @author liw
* @date 2020-07-01
*/
public final class Singleton {
//实例变量
private byte[] data = new byte[1024];
//定义实例,但不直接初始化
private static Singleton instance = null;
//私有构造
private Singleton() {
}
public static Singleton getInstance() {
if (null == instance) {
instance = new Singleton();
}
return instance ;
}
}
所谓懒汉式就是在使用实例的时候再去创建(用时创建)。但是多线程的情况下是懒汉式是线程不安全的。懒汉式可以保证实例的懒加载,但是无法保证实例的唯一。
懒汉式 + 同步方法
package day0518;
/**
* @author liw
* @date 2020-07-01
*/
public final class Singleton {
//实例变量
private byte[] data = new byte[1024];
//定义实例,但不直接初始化
private static Singleton instance = null;
//私有构造
private Singleton() {
}
//getInstance 方法加入同步控制
public static synchronized Singleton getInstance() {
if (null == instance) {
instance = new Singleton();
}
return instance ;
}
}
采用懒汉式+数据同步的方式既满足了懒加载又能百分百的保证instance 实例的唯一性,但是synchronized 关键字天生的排他性导致getInstance 方法只能在同一时刻被一个线程访问,性能低下。
Double-Check
package day0518;
import java.net.Socket;
import java.sql.Connection;
/**
* @author liw
* @date 2020-07-01
*/
public final class Singleton {
//实例变量
private byte[] data = new byte[1024];
//定义实例,但不直接初始化
private static Singleton instance = null;
Connection conn;
Socket socket;
//私有构造
private Singleton() {
//对conn和socket进行初始化
}
//getInstance 方法加入同步控制
public static synchronized Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}
很聪明的一个方式通过两次校验,第一次判断当instance 为null的时候进入同步代码块,避免每次都需要进入同步代码快;第二次校验instance 为空则为其创建实例。但是这种方式在多线程的情况下有可能会造成空指针异常。有可能在成员变量实例化conn 、socket的发生在instance实例化之后。
Volatile +Double-Check
只需要在Double-Check 的模式代码上加个volatile。
package day0518;
import java.net.Socket;
import java.sql.Connection;
/**
* @author liw
* @date 2020-07-01
*/
public final class Singleton {
//实例变量
private byte[] data = new byte[1024];
//定义实例,但不直接初始化
private static volatile Singleton instance = null;
Connection conn;
Socket socket;
//私有构造
private Singleton() {
//对conn和socket进行初始化
}
//getInstance 方法加入同步控制
public static synchronized Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}
Holder 方式
package day0518;
import java.net.Socket;
import java.sql.Connection;
/**
* @author liw
* @date 2020-07-01
*/
public final class Singleton {
//实例变量
private byte[] data = new byte[1024];
//私有构造
private Singleton() {
}
private static class Holder {
private static Singleton instance = new Singleton();
}
//getInstance 方法加入同步控制
public static synchronized Singleton getInstance() {
return Holder.instance;
}
}
Holder方式完全是借助了类的加载的特点。
在Singleton 类中并没有instance 的静态成员,而是将其放到了静态内部类Holder中,因此在Singleton 类的初始化过程中比不过不不会创建Singleton 的实例,Holder 类中定义了Singleton 的静态变量,并且直接进行了实例化,当Holder 被主动使用的时候则会创建Singleton 的实例,Singleton 实例的创建过程在java程序编译时期收集到()方法中,该方法又是同步方法,可以保证内存的可见性、jvm指令的顺序性和原子性。Holder方式的单例是最好的设计之一,也是目前使用比较广的设计。
枚举方式