单例模式三个要素:
1.私有构造函数,防止其他代码实例化对象;
2.提供一个属性,保存那个唯一的实例化对象;
3.提供一个公共方法,以便别人获取到实例化对象;
饿汉式
public class Singleton01 {
// 私有化构造函数
private Singleton01() {}
// 静态属性,指向单例对象
private static final Singleton01 INSTANCE = new Singleton01();
//公共方法,以便别人获取对象
public static Singleton01 getInstance() {
return INSTANCE;
}
}
优点:写法简单,线程安全。
缺点:消耗资源,不管对象是否会用到,都会实例化对象出来。
懒汉式
public class Singleton02{
// 私有化构造函数
private Singleton02(){}
// 静态属性,指向单例对象
private final final Singleton02 INSTANCE;
// 公共方法,以便别人获取对象
public static Singleton02 getInstance(){
if(INSTANCE == null){
INSTANCE = new Singleton02();
}
return INSTANCE;
}
}
优点:写法简单,节省资源,只有用到这个对象的时候才会进行实例化。
缺点:存在线程安全问题。
简单加锁
public class Singleton02 {
// 私有化构造函数
private Singleton02() {}
// 静态属性,指向单例对象
private static Singleton02 INSTANCE;
// 加了synchronized关键字
public synchronized static Singleton02 getINSTANCE() {
if (INSTANCE == null) {
INSTANCE = new Singleton02();
}
return INSTANCE;
}
}
优点:线程安全,写法简单。
缺点:每次获取对象都进行加锁,存在性能问题。
双重检查
public class Singleton03{
// 私有化构造函数
private Singleton03() {}
// 静态属性,指向单例对象(注意这里加了volatile关键字,否则在多线程情况下指令重排会出问题)
private static volatile Singleton03 INSTANCE;
public static Singleton03 getINSTANCE() {
// 只有在创建新对象的时候才进行加锁
if (INSTANCE == null) {
synchronized (Singleton03.class){
// 这里还要再判断一下,否则锁释放之后其他线程还是要实例化对象
if (INSTANCE == null){
INSTANCE = new Singleton03();
}
}
}
return INSTANCE;
}
}
优点:线程安全,相比上一种节约资源
缺点:已经很美好了,已经大大减少了获取锁的次数,不过还是可以更好。
静态内部类
public class Singleton04 {
// 私有化构造函数
private Singleton04() {}
// 在内部类声明一个静态属性
private static class InnerHolder {
static final Singleton04 INSTANCE = new Singleton04();
}
// 公共方法
public static Singleton04 getInstance() {
return InnerHolder.INSTANCE;
}
}
因为JVM保证了内部类的线程安全,即一个内部类在整个程序中不会被重复加载,并且如果你没有使用到内部类的话,是不会加载这个内部类的。这就非常巧妙的实现了线程安全以及节约资源的好处!
优点:节约资源、线程安全、性能好
缺点:会被序列化或者反射破坏单例
以上的几种写法均存在被序列化或反射破坏的情况,可以进行一下优化
public class Main implements Serializable, Cloneable {
private static final long serialVersionUID = 6125990676610180062L;
private static Main main;
private static boolean isFirstCreate = true;
private Main(){
/*防止反射破坏单例*/
if(isFirstCreate){
synchronized (Main.class) {
if(isFirstCreate){
isFirstCreate = false;
}
}
}else{
throw new RuntimeException("已经实例化一次, 不能再实例化");
}
}
public static Main getInstance(){
if (main == null) {
synchronized (Main.class) {
if (main == null) {
main = new Main();
}
}
}
return main;
}
/*防止克隆破坏单例*/
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return main;
}
/*防止序列化破坏单例*/
private Object readResolve() {
// TODO Auto-generated method stub
return main;
}
public static void main(String[] args) throws Exception{
Main main = Main.getInstance();
System.out.println("singleton的hashCode:"+main.hashCode());
//通过克隆获取
Main clob = (Main) Main.getInstance().clone();
System.out.println("clob的hashCode:"+clob.hashCode());
//通过序列化,反序列化获取
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(Main.getInstance());
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
Main serialize = (Main) ois.readObject();
if (ois != null) ois.close();
if (bis != null) bis.close();
if (oos != null) oos.close();
if (bos != null) bos.close();
System.out.println("serialize的hashCode:"+serialize.hashCode());
//通过反射获取
Constructor<Main> constructor = Main.class.getDeclaredConstructor();
constructor.setAccessible(true);
Main reflex = constructor.newInstance();
System.out.println("reflex的hashCode:"+reflex.hashCode());
}
}
枚举实现
一种简单巧妙的方法,根本不存在构造方法,当然也就不会被反射和序列化破坏
public enum Singleton05 {
// 实例
INSTANCE;
// 公共方法
public Singleton05 getInstance() {
return INSTANCE;
}
}