单例模式
使用懒汉式加载。 最终可以做到 线程安全,延迟加载,效率高的特点。
volatile 简介
package oneDay;
/**
* 保证一个类仅有一个实例,并提供一个访问它的全局访问点
* 1:构造方法私有化
* 2:声明一个本类对象
* 3:给外部提供一个静态方法获取对象实例
* <p>
* <p>
* 单例设计模式的存在是为了什么?
* 1:在设计一些工具类的时候,不需要属性这些东西,只需要方法就可以,所以就不掺和对象,只用单例就可以.
* 2:工具类可能会被频繁调用.
* 目的是为了节省重复创建对象所带来的内存消耗.从而提高效率.
* <p>
* 能不能使用构造方法私有化再加上static 来替代单例? 不能, 构造方法 不能静态化.
* <p>
* <p>
* 在类被加载的时候 因为类中的实例是有 static修饰的,所以当时的实例已经创建好了.
* 然后继续getIntance的时候 得到的就是 同一个对象.
*/
public class singleCase {
public static void main(String[] args) {
}
}
// 饿汉式 : 在类被加载后,对象被创建,到程序结束之后释放.
class Singleton1 {
private Singleton1() {
}
private static Singleton1 s = new Singleton1();
public static Singleton1 getInstance() {
return s;
}
public void print() {
System.out.println("测试方法1!");
}
}
// 懒汉式 : 在第一次调用getInstance方法时,对象被创建,到程序结束后释放.
// 在多线程访问的时候 可能会因为生命周期的问题 而出现程序崩溃错误.
// 可能出现 第一个线程进入 getInstance的时候, 发现s为空 , 在没有 声明实例的时候, 第二个进程也发现了 s 为空. 这个时候 实例就变了.
// 解决方法:
// 1: 将getInstance加上线程锁, 但是这样的话就是 串行执行了 . 效率的良心大大滴坏了.
// 2: 还有一种说话 是给 s = new Singleton2(); 加上同步代码块, 但是这样显然没法解决 实例变化的问题. 是个扯淡的方法.
class Singleton2 {
private Singleton2() {
}
private static Singleton2 s;
public static Singleton2 getInstance() {
if (s == null)
s = new Singleton2();
return s;
}
public void print() {
System.out.println("测试方法2!");
}
}
// 3: 结合第二种方法, 实现第三种方法 . 加上同步代码块减少锁影响的颗粒大小, 并且避免第二种方法的问题. 就是在同步代码块中 再次判断是否为空.
// 双重加锁法. 但是这样还可能会存在问题. 因为牵扯到 JVM实现 s = new Singleton3(); 的指令顺序问题 .
// 其指令大致分为以下三步: 1 申请一块内存空间用于存放实例内容 2 在该空间实例化对象 3 将该内存地址富裕s
// 底层执行的时候 不一定是按照123 执行的, 有可能是132 (底层代码速度优化方案.). 这样的话 在没有实例化对象的时候 , s不为null , 这样另一个线程发现s 不为空, 但是拿到的是空的s , 这个时候程序就炸了.
class Singleton3 {
private Singleton3() {
}
private static Singleton3 s;
public static Singleton3 getInstance() {
if (s == null)
synchronized (Singleton3.class) {
/* 第三种方案对 对二种方法的改进 */
if (s == null) {
s = new Singleton3();
}
}
return s;
}
public void print() {
System.out.println("测试方法2!");
}
}
// 4: 为了解决第三种方案的缺陷, 我们需要禁止代码重排. 由此而生第四种方案, 这个应该是最完美的方案了.
class Singleton4 {
private Singleton4() {
}
/* 第四种方案对第三种的优化 */
private static volatile Singleton4 s;
public static Singleton4 getInstance() {
if (s == null)
synchronized (Singleton4.class) {
/* 第三种方案对 对二种方法的改进 */
if (s == null) {
s = new Singleton4();
}
}
return s;
}
public void print() {
System.out.println("测试方法2!");
}
}
使用饿汉式配合类装载机制,也可以实现懒加载和线程安全,以及高效。
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
类加载器加载类的时候,会将静态属性直接初始化。 通过这里类加载器的特性, 我们可以做到线程安全。 通过内部类的方式,实现懒加载。