单例设计模式
单例设计模式
单例模式:
定义:某个类在某个系统中只能有一个实例化对象被获取和使用
实现要点: 1.构造器私有
2.含有一个该类的静态变量保存这个唯一实例
3.对外提供获取该实例对象的方式
分类:1.饿汉式 2.懒汉式
下面对这两种单例模式展开分析,进行对比
饿汉式
1.代码实现1(静态常量方式实现)
public class SingleTon{
private static final SingleTon singleTon = new SingleTon();
private SingleTon(){}
public static SingleTon getSingleTon(){
return singleTon;`
}
}
2.代码实现2(静态代码块方式实现)
public class SingleTon{
private staic SingleTon;
//在代码块执行时,创建单例对象
static {
singleTon = new SingleTon();
}
private SingleTon(){}
public static SingleTon getSingleTon(){
return singleTon;
}
}
public class TestSingleTon{
public static void main(Stirng[] args){
SingleTon s1 = SindleTon.getSingleTon;
SingleTon s2 = SindleTon.getSingleTon;
System.out.println(s1 == s2); //true
}
}
3.特点及代码分析
1)私有化构造器
2)在类的内部创建一个类的实例,static
3)私有化对象,通过公共方法调用
4)此方法只能通过类来调用,因为是static的,类的实例也是static的
5)只创建一个对象,不会有线程不安全的情况
6)在类加载是就完成了类实例化,避免了线程同步问题,但没有达到Lazy Loading的效果,如果没有使用过这个对象,会造成内存浪费
7)如何避免这种情况发生呢?可以采用枚举形式实现单例模式
代码块如下:
public enum SingleTon{
INSTANCE;
}
懒汉式
1.代码实现1(线程不安全方式)
public class SingleTon{
private SingleTon(){}
private static SingleTon singleTon = null;
//当调用方法时,才创建单例对象
public static SingleTon getSingleTon(){
if(singleTon == null){
singleTon = new SingleTon();
}
return singleTon;
}
}
2.代码分析1
1)起到了lazy Loading的作用,即延迟加载对象,但只能在单线程时使用
2)如果在多个线程下,一个线程进入了if(singleTon == null)判断语句时,若满足条件判断且还没来得及继续执行,另一个线程也进入到if(singleTon == null)判断语句,这就会产生多个实例对象,即线程不安全。
3.代码实现2(线程安全方式\双重检查)
public class SingleTon{
private SingleTon(){}
private static SingleTon singleTon = null;
//当调用方法时,才创建单例对象
public static SingleTon getSingleTon(){
if(singleTon == null){//第一层检查,检查是否有引用对象,如果一个线程获取了实例,则不需要进入同步代码块中了
synchronized (SingleTon.class){//第一层锁,保证只有一个线程进入。同步代码块使用的锁是单例的字节码文件对象,且只能用这个锁
if(singleTon == null){ //第二层检查
singleTon = new SingleTon();
}
}
}
return singleTon;
}
4.代码分析2
//volatile关键字的作用为禁止指令重排,保证返回singleTon对象一定在创建对象后
singleTon = new SingleTon();该语句的底层实现逻辑为:
(1)在堆上开辟空间
(2)属性初始化
(3)引用指向对象
//假设以上三个内容为三条单独指令,因指令重排可能会导致执行顺序为1->3->2(正常为1->2->3),当单例模式中存在普通变量需要在构造方法中进行初始化操作时,单线程情况下,顺序重排没有影响;但在多线程情况下,假如线程1执行singleton=new Singleton();语句时先1再3,由于系统调度线程2的原因没来得及执行步骤2,但此时已有引用指向对象也就是singleton!=null,故线程2在第一次检查时不满足条件直接返回singleton,此时singleton为null(即str值为null)
//volatile关键字可保证singleton=new Singleton();语句执行顺序为123,因其为非原子性依旧可能存在系统调度问题(即执行步骤时被打断),但能确保的是只要singleton!=0,就表明一定执行了属性初始化操作;而若在步骤3之前被打断,其他线程可进入第一层检查向下执行创建对象.此时线程2拿到的不是一个null singleton,而是一个没有被步骤2正确初始化的singleton。
5.代码实现3(静态内部类)
//懒汉式:静态内部类形式
public class SingleTon {
private SingleTon(){
}
private static class Inner{
private static final SingleTon SINGLE_TON = new SingleTon();
}
public static SingleTon getSingleTon(){
return Inner.SINGLE_TON;
}
}
//分析:(1)只有在调用方法时,才会加载到内部类,从而完成类的实例化,singleTon。
(2)避免了线程不安全,利用静态内部类特点实现延迟加载,效率高。
6.实际应用
public class LazySingleDesign {
private static LazySingleDesign lazySingleDesign = null ;
private LazySingleDesign(){}
public static LazySingleDesign getInstance(){
synchronized(LazySingleDesign.class){
if(lazySingleDesign == null){
lazySingleDesign = new LazySingleDesign();
}
}
return lazySingleDesign;
}
}
@Test
public void test() {
ExecutorService executor = Executors.newFixedThreadPool(10);
for(int i = 0 ; i < 10;i++){
executor.execute(new Runnable() {
@Override
public void run() {
LazySingleDesign user = LazySingleDesign.getInstance();
System.out.println("design = " + user);}});
}
}
//getInstance()方法内的第一个if判断可以去掉,生成的也是单例。另外可见性可以去掉也不影响生成的单例。
参考文章链接:1.https://blog.csdn.net/weixin_42617262/article/details/90448083
2.https://big-data.blog.csdn.net/article/details/83422780?spm=1001.2101.3001.6650.9&utm_medium=distribute.pc_relevant.none-task-blog-2defaultBlogCommendFromBaiduRate-9-83422780-blog-115265060.pc_relevant_3mothn_strategy_recovery&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2defaultBlogCommendFromBaiduRate-9-83422780-blog-115265060.pc_relevant_3mothn_strategy_recovery&utm_relevant_index=16
3.https://blog.csdn.net/qq_42804736/article/details/115265060

浙公网安备 33010602011771号