前置知识:
一、volatile解决内存可见性
首先明确什么是内存可见性:
假如有两个线程t1和t2,t1频繁读取主内存,效率比较低,就被优化成直接读自己的工作内存;t2修改了主内存的结果,由于t1没有读主内存,导致修改不能被识别到
volatile就告诉计算机你不要优化,会直接从变量内存地址中读取数据,从而可以提供对特殊地址的稳定访问。
正题:
一、单例模式是实际开发中比较常用的一种模式,实现方法也五花八门,在这我主要介绍2种比较经典的模式
1、饿汉模式(急迫)
2、懒汉模式(从容)
举个简单的例子:假如老师布置了十门作业
饿汉模式就是把所有的作业都写完,即使很多;而懒汉模式就是如果明天只检查三门我就只写三门作业
在实际开发中当我们某个资源有限的情况,把所有的任务都完成是没有必要的,只需完成最近需要的就可
(1)下面首先先完成饿汉单例模式的创建:
class Singleton {
// 唯一实例的本体
private static Singleton instance = new Singleton();
// 获取到实例的方法
public static Singleton getInstance() {
return instance;
}
// 禁止外部 new 实例.
private Singleton() { }
}
public class ThreadDemo17 {
public static void main(String[] args) {
// 此时 s1 和 s2 是同一个对象!!
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
}
}
private static Singleton instance = new Singleton();
被static修饰,该属性是类属性,在JVM中,每个类对象只有一份,类对象这个成员也只有一份。但要保证是唯一实例本体,就必须禁止new外部对象。
private Singleton() { }
这样就保证了在内部实例好对象的同时紧张外部new新的对象,保证了唯一实例本体,也就是单例。
(2)懒汉模式实现单例
核心思想:非必要,不创建(多的活一份不干)
class SingletonLazy{
private static SingletonLazy instance=null;
public static SingletonLazy getInstance(){
if(instance==null){
synchronized (SingletonLazy.class){
if(instance==null){
instance=new SingletonLazy();
}
}
}
return instance;
}
private SingletonLazy(){}
}
public class Demo8 {
public static void main(String[] args) {
SingletonLazy s1=SingletonLazy.getInstance();
SingletonLazy s2=SingletonLazy.getInstance();
System.out.println(s1==s2);
}
}
!!!!!多个线程下调用getInstance,是否会出现安全问题???
饿汉模式,也就是(1),认为线程是安全的,只是读数据,而没有修改数据;
而(2)修改了数据,可能会导致多个线程修改一个变量,进而导致线程不安全问题,下面链接的第二条
线程不安全的原因(实际开发中经常出bug的地方)-CSDN博客
那么如何解决呢,利用synchronized(加锁)来解决,也就是上述代码,那么这个代码真的很好嘛,网上百分之九十以上的博客是这么教你的!!!但是还有一个小小的问题,即使发生的概率很小,但如果在实际开发中可能就会造成bug!!!!!
instance=new SingletonLazy();
这一步就会可能导致指令重排序!!!
解决方法也很简单,利用volatile
private static SingletonLazy instance=null;
//加上volatile
volatile private static SingletonLazy instance=null;
所以,最终代码如下:!!!!
class SingletonLazy{
volatile private static SingletonLazy instance=null;
public static SingletonLazy getInstance(){
if(instance==null){
synchronized (SingletonLazy.class){
if(instance==null){
instance=new SingletonLazy();
}
}
}
return instance;
}
private SingletonLazy(){}
}
public class Demo8 {
public static void main(String[] args) {
SingletonLazy s1=SingletonLazy.getInstance();
SingletonLazy s2=SingletonLazy.getInstance();
System.out.println(s1==s2);
}
}
小结:
单例模式,线程安全问题
饿汉模式:天然就是安全的,只是读操作
懒汉模式:不安全 有读也有写
1、加锁(synchronized),把if和esle变成原子操作
2、双重if,减少不必要的加锁操作
3、使用volatile禁止指令重排序,保证后续线程拿到的是完整对象