概述:单例模式就是在整个软件系统中,对于某个类只能存在一个对象实例,而且只提供了一个获取该对象实例的方法(静态方法)
1)单例模式一共有8种实现的方式
①饿汉式(静态常量)
②饿汉式(静态代码块)
③懒汉式(线程不安全)
④懒汉式(线程安全:同步方法)
⑤懒汉式(线程安全:同步代码块)
⑥双重检查
⑦静态内部类
⑧枚举
一 饿汉式(静态变量)
优点:写法简单,在类装载的时候就已经完成了实例化,避免了线程安全问题
缺点:在装载的时候完成了实例化,没有达到懒加载的效果,如果自始至终都没有使用过整个对象,则会造成内存浪费
public class Single1 {
// 初始创建唯一的对象实例
private Single1 instance = new Single1();
// 构造器私有化
private Single1(){}
// 公共方法,返回单例对象
public Single1 getInstance() {
return instance;
}
}
二 饿汉式(静态代码块)
这种方式和上一个实现类似,也是在类装载的时候进行对象的实例化过程,但是把这过程放在了静态代码块里头
结论:可用,也可能造成内存的浪费
public class Single2 {
// 1、私有化构造器
private Single2() {
}
// 2、静态变量
private static Single2 instance;
static {
instance = new Single2();
}
// 3、公共方法
public static Single2 getInstance() {
return instance;
}
}
三 懒汉式(线程不安全)
在需要获取对象时,才去判断内存中是否已经存在此对象
优点:起到了懒加载的效果,但是只能在单线程下使用
缺点:在多线程的情况下,容易会生成多个对象实例,线程不安全
总结:在实际开发中,不要使用
public class Single3 {
private static Single3 instance;
private Single3() {
}
public static Single3 getInstance() {
if (instance == null) {
instance = new Single3();
}
return instance;
}
}
四 懒汉式(线程安全:同步方法)
给方法加锁确实是解决了线程安全问题,但是又引发了另一个问题:效率低下,因为每一个人想使用这个方法,都要去判断是否握有这个锁,如果当前有人不释放锁,则需要等待
总结:在开发中,也不推荐使用
public class Single4 {
private static SIngle4 instance;
private SIngle4() {
}
//在方法加锁,只有握有此锁的对象才能进入
public static synchronized SIngle4 getInstance() {
if (instance == null) {
instance = new SIngle4();
}
return instance;
}
}
五 懒汉式(线程安全:同步代码块)
这是对上一种方法的改进,效率高一些
public class Single5 {
private static SIngle5 instance;
private SIngle5() {
}
public static SIngle5 getInstance() {
if (instance == null) {
// 同步代码块,锁为类
// 为什么同步代码块中还需要判断呢,因为以防外一,
// 有两个人同时第一时间都需要创建对象,而此时则需要判断
synchronized (SIngle5.class) {
if (instance==null){
instance = new SIngle5();
}
}
}
return instance;
}
}
六 双重检查
public class Single5 {
private static SIngle5 instance;
private SIngle5() {
}
public static SIngle5 getInstance() {
if (instance == null) {
// 同步代码块,锁为类
// 为什么同步代码块中还需要判断呢,因为以防外一,
// 有两个人同时第一时间都需要创建对象,而此时则需要判断
synchronized (SIngle5.class) {
if (instance==null){
instance = new SIngle5();
}
}
}
return instance;
}
}
七 静态内部类
这种方式采用类装载的机制来保证初始化时只有一个线程
静态内部类 Singleton 在 SIngle7 装载的时候并不会立即实例化,而是在需要时,调用getInstance 方法才会装载
类的静态属性保证了只会在第一次的加载类的时候初始化,jvm帮助保证了线程的安全,在类进行初始化时,其他线程时无法进入的
总结:线程安全,实现了懒加载,效率高,推荐使用
public class Single7 {
private static SIngle7 instance;
private SIngle7() {
}
//静态内部类,有一个静态属性
private static class Singleton {
private static final SIngle7 INSTANCE = new SIngle7();
}
//静态方法,直接返回静态内部类的属性
public static SIngle7 getInstance() {
return Singleton.INSTANCE;
}
}
八 枚举
使用枚举,可以实现单例,这是JDK5新增的实现方式
不仅能避免线程同步问题,而且还能防止反序列化重新创建新的对象
这是 Effective Java 作者提倡的方法
public enum Single8 {
INSTANCE;
}