我是 [闲人球] 这个昵称是由我的梦想(闲云野鹤)+外号(球球)组合而成。我是一个记性成谜的女孩儿,记不住公式记不住代码却能背曲谱,想来还是疼痛的记忆比较深刻。
这博客是对自己学习的一丢丢总结及记录,如果您对 Java、算法、架构感兴趣,可以关注我的动态,我们一起学习。
快乐学习,认真生活;心中有爱,眼里有光
鉴于我记性实在不咋地,备注解释的贼详细,小白一看就懂,话不多说,上代码
/**
* 饿汉式
* 类加载到内存后,就实例化一个单例,JVM保证线程安全
* 唯一缺点:不管用到与否,类加载时就完成实例化
*/
public class Singleton01 {
private static final Singleton01 INSTANCE = new Singleton01();
private Singleton01() {
}
public static Singleton01 getInstance() {
return INSTANCE;
}
public static void main(String[] args) {
Singleton01 t1 = Singleton01.getInstance();
Singleton01 t2 = Singleton01.getInstance();
System.out.println(t1 == t2);
}
}
/**
* lazy loading
* 懒汉式
* 达到了按需初始化的目的,但这种方式在多线程访问的时候是有影响的
*/
private static Singleton01 INSTANCE;
private Singleton01() {
}
public static Singleton01 getInstance() {
/*
分析:
第一个线程进来后,在还没有new的时候,
第二个线程来了,判断INSTANCE还是为null,它继续执行new Singleton01(),
然后第一个线程继续执行,又去new Singleton01(),
所以INSTANCE 在两个线程中已经不再是一个实例了。
*/
if (INSTANCE == null) {
// try {
// Thread.sleep(1);//增加线程被打断的几率
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
INSTANCE = new Singleton01();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(Singleton01.getInstance());
}).start();
}
}
测试结果:全乱了,咋办呢?加锁嘛!
// 可以通过加synchronized来解决,但效率就降低了
public static synchronized Singleton01 getInstance() {
if (INSTANCE == null) {
try {
Thread.sleep(1);//增加线程被打断的几率
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Singleton01();
}
return INSTANCE;
}
效率降低了,肯定又要想怎么提高效率嘛!传说中的完美写法来啦
人啊最大的特点就是,精(闲)益(的)求(没)精(事)
这里有个坑噢???
DCL(Double Check Lock),一定要加上volatile哦,volatile的两个特点,可见性、有序性
Java的虚拟机内部执行的时候,会对java汇编语言进行优化,会有语句重排
private static volatile Singleton01 INSTANCE;
private Singleton01() {
}
public static Singleton01 getInstance() {
// 双重检查
if (INSTANCE == null) {
synchronized (Singleton01.class) {
if(INSTANCE==null) {
try {
Thread.sleep(1);//增加线程被打断的几率
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Singleton01();
}
}
}
return INSTANCE;
}
/**
* 静态内部类方式
* 加载外部类时不会加载内部类,这样可以实现懒加载 JVM保证线程安全
* 虚拟机加载class时只会加载一次,因此只有一个实例
*/
private Singleton01() {
}
private static class SingletonHolder{
private static final Singleton01 INSTANCE = new Singleton01();
}
public static Singleton01 getInstance(){
return SingletonHolder.INSTANCE;
}
最后是java大牛,java创始人之一 Joshua J. Bloch 约书亚·布洛克,他在《Effective Java》这本书中写了一种单例是这样式滴
大牛内心os:你们这些渣渣,一个单例整的这么麻烦,实在是看不下去了…
/**
* 不仅可以解决线程问题,还可以防止反序列化
* 为啥可以防反序列化呢?因为枚举没有构造方法
*/
public enum Singleton02 {
INSTANCE;
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(Singleton02.INSTANCE.hashCode());
}).start();
}
}
}
综上所述,除大牛的堪称perfect外(但俺不会用啊),其它越写越复杂,俺就觉得饿汉挺好的,简单又实用。
工作中啥方式好就用啥,不必追求过多的完美,有时候不完美才是最美的。
果然大半夜的容易写bug啊,取的这名字我都无语了,若发现类名不一样,那可能是漏改了,请见谅…
最后祝各位大佬 心中有爱,眼里有光;快乐学习,认真生活。