多线程下的单例模式

本文详细介绍了Java中单例模式的实现方式,包括饿汉模式和懒汉模式,并通过多线程环境下的测试验证了其正确性和效率问题,探讨了不同实现方式下单例模式的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设计模式之(单例模式)


java的单例模式是一种常见的设计模式,单例模式的写法有好几种,主要分为懒汉式单例,饿汉式单例还有登记式单例

下面我们要介绍的就是多线程下的单例模式,多线程,我们以继承Thread和实现Runnable接口为主


首先我们以饿汉模式为例

package lock;

public class EhanSingleton {
    
    /*饿汉加载模式/立即加载模式*/
    
    
    //初始化构造函数
    private  EhanSingleton(){
        
    }
    private  static EhanSingleton  ehan = new EhanSingleton();
    
    public static  EhanSingleton getInstance(){
        try {
            Thread.sleep(3000);   //方便多线程测试
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
          return  ehan ;
    }

}

然后我们创建多个线程来测试一下

package lock;

public class MyThread  extends  Thread{
       
     @Override
    public void run() {
             System.out.println(EhanSingleton.getInstance());
         
    }
     public static void main(String[] args) {
            MyThread m1 = new MyThread();
            MyThread m12 = new MyThread();
            MyThread m13 = new MyThread();
            
            m1.start();
            m12.start();
            m13.start();
    }
}

测试结果为:

cn.spring.dxcDanli.Singleton@132965a8
cn.spring.dxcDanli.Singleton@132965a8
cn.spring.dxcDanli.Singleton@132965a8

显示为三个相同的内存地址,说明这个模式符合单例


第二种模式为懒汉模式

package lock;

public class LanHanSingleton {
    
    /*懒汉模式/延迟加载*/
    
    //私有化构造函数
    private LanHanSingleton(){
        
    }
    
    private  static LanHanSingleton lanHanSingleton ; 
    
    public  static  LanHanSingleton  getInstance() {
           if(lanHanSingleton == null){
                try {
                    Thread.sleep(3000);
                     lanHanSingleton = new LanHanSingleton() ;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
              
           }
          return lanHanSingleton ;  
    }

}
我在用多线程去测试一下是否每个对象的HashCode的值是保持一致的

package lock;

public class MyThread  extends  Thread{
       
     @Override
    public void run() {
             System.out.println(LanHanSingleton.getInstance().hashCode());
         
    }
     public static void main(String[] args) {
            MyThread m1 = new MyThread();
            MyThread m12 = new MyThread();
            MyThread m13 = new MyThread();
            
            m1.start();
            m12.start();
            m13.start();
    }
}

测试结果如下:

126720696
137014984
1638443495

测试的结果发现 这个已经不符合单例模式,他们并不是同一个对象了,而是几个不同的对象,所以这种懒汉模式在单线程中是符合单例模式的,不过在多线程环境中是不符合单例模式

 

想到这里,大家肯定会想到了synchrinized关键字,我们在来看看效果

package lock;

public class LanHanSingleton {
    
    /*懒汉模式/延迟加载*/
    
    //私有化构造函数
    private LanHanSingleton(){
        
    }
    
    private  static LanHanSingleton lanHanSingleton ; 
    
    public synchronized  static  LanHanSingleton  getInstance() {
           if(lanHanSingleton == null){
                try {
                    Thread.sleep(3000);
                     lanHanSingleton = new LanHanSingleton() ;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
              
           }
          return lanHanSingleton ;  
    }

}
测试结果如下

1638443495
1638443495
1638443495

大家发现这样的确可以解决多线程带来的不同对象所导致的问题,但是这个方法并不好,这种方法效率非常低下,一定要等到上一个线程释放锁以后才能获取对象

同步方法是对整个方法持有锁,这个对于效率来说实在太慢,大家还会想到用同步块,那么我们在试一试

package lock;

public class LanHanSingleton {
    
    /*懒汉模式/延迟加载*/
    
    //私有化构造函数
    private LanHanSingleton(){
        
    }
    
    private  static LanHanSingleton lanHanSingleton ; 
    
    public   static  LanHanSingleton  getInstance() {
           try {
             synchronized (LanHanSingleton.class) {
                 if(lanHanSingleton == null){
                        Thread.sleep(3000);
                         lanHanSingleton = new LanHanSingleton() ;
               }
            }   
          
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
              
          return lanHanSingleton ;  
    }

}

其实这个效果和上个效果差不多,效率都是比较慢的,和同步方法synchronized一样是同步运行的

这里最好的方式就是DCL双检查锁机制,也就是同步代码块

package lock;

public class LanHanSingleton {
    
    /*懒汉模式/延迟加载*/
    
    //私有化构造函数
    private LanHanSingleton(){
        
    }
    
    private  static LanHanSingleton lanHanSingleton ; 
    
    public   static  LanHanSingleton  getInstance() {
           try {
            
                 if(lanHanSingleton == null){
                     synchronized (LanHanSingleton.class) {
                         if(lanHanSingleton == null){
                             lanHanSingleton = new LanHanSingleton() ;
                         }
                        
                     }
               }
          
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
              
          return lanHanSingleton ;  
    }

}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值