单例模式
- 这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式
- 种模式涉及到一个单一的类,该类负责创建自己的对象, 同时确保只有单个对象被创建
- 这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给所有其他对象提供这一实例
- 对象:对象是类的一个实例,有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
- 类:类是一个模板,它描述一类对象的行为和状态。
- 方法:方法就是行为,一个类可以有很多方法。逻辑运算、数据修改以及所有动作都是在方法中完成的。
- 实例变量:每个对象都有独特的实例变量,对象的状态由这些实例变量的值决定。
优点:
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
2、避免对资源的多重占用
缺点:
没有接口,不能继承,与单一职责原则冲突
一个类应该只关心内部逻辑,而不关心外面怎么样来实例化
volatile
1. 保证变量的内存可见性
这里大家应该有个疑问是,为什么加锁后就保证了变量的内存可见性了?
因为当一个线程进入 synchronizer 代码块后,线程获取到锁,会清空本地内存,然后从主内存中拷贝共享变量的最新值到本地内存作为副本,执行代码,又将修改后的副本值刷新到主内存中,最后线程释放锁。
这里除了 synchronizer 外,其它锁也能保证变量的内存可见性。
2.禁止指令重排序
什么是重排序?
- 为了提高性能,在遵守 as-if-serial 语义(即不管怎么重排序,单线程下程序的执行结果不能被改变。编译器,runtime 和处理器都必须遵守。)的情况下,编译器和处理器常常会对指令做重排序。
- 一般重排序可以分为如下三种类型:
1.编译器优化重排序
- 编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
2.指令级并行重排序
- 现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
3.内存系统重排序
- 由于处理器使用缓存和读 / 写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。
饿汉式单例
//饿汉式单例
public class Hungry {
//可能浪费空间
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
//构造器私有
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
懒汉式单例
+volatile (volatile 保证了不同线程对共享变量操作的可见性,也就是说一个线程修改了 volatile 修饰的变量,当修改后的变量写回主内存时,其他线程能立即看到最新值。)
//懒汉式单例模式
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName() + "ok");
}
private volatile static LazyMan lazyMan;
//双重检测锁模式 懒汉式单例 DCL 懒汉式
public static LazyMan getInstance() {
if(lazyMan==null){
synchronized (LazyMan.class){
if (lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
}
}
}
return lazyMan;
}
/**
* 分配内存空间 1
* 执行构造方法,初始化对象 2
* 把对象指向这个空间 3
*
*
*
*/
}
原子性问题
- 所谓的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行。
- 在多线程环境下,volatile 关键字可以保证共享数据的可见性,但是并不能保证对数据操作的原子性。也就是说,多线程环境下,使用 volatile 修饰的变量是线程不安全的。
静态内部类
//静态内部类
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
枚举
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}