java 单例模式与多线程

Posted ty_laurel

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java 单例模式与多线程相关的知识,希望对你有一定的参考价值。

什么是单例模式

单例模式是一种创建型模式,某个类在采用了单例模式,在该类创建后,只能产生一个实例供外部访问,并且提供一个全局的访问点。数据库连接池的设计一般就是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要就是节省打开或关闭数据路连接所引起的效率损耗,使用单例模式,就可以大大降低这些损耗

饿汉模式

饿汉模式也称为立即加载,即就是使用类的时候已经将对象创建完毕,常见的实现办法就是直接new实例化,也就是在调用方法前,实例已经被创建了,如下实例。

 
  
   public class MyObject 
  
  
       //立即加载方式/饿汉模式
  
  
       private static MyObject myObject = new MyObject();
  
  
       private MyObject() 
  
  
       public static MyObject getInstance() 
  
  
           return myObject;
  
  
       
  
  
   
  
 

这种方式就是单例模式的实现,在以后的使用中只需要调用MyObject.getInstance()方法即可。对于这种模式,使用多线程实现调用。

 
  
   //创建线程类
  
  
   public class MyThread extends Thread 
  
  
       public void run() 
  
  
           System.out.println(MyObject.getInstance().hashCode());
  
  
       
  
  
   
  
 

创建运行类,创建三个线程去分别调用MyObject类中的getInstance方法,也就是MyObject的实例:

 
  
   //运行类
  
  
   public class Run 
  
  
       public static void main(String[] args) 
  
  
           MyThread t1 = new MyThread();
  
  
           MyThread t2 = new MyThread();
  
  
           MyThread t3 = new MyThread();
  
  
    
  
  
           t1.start();
  
  
           t2.start();
  
  
           t3.start();
  
  
       
  
  
   
  
 

运行三个线程,可以看到结果输出了三个相同的hashcode,则说明三个线程就是调用的同一个对象实例。

 
  
   1065473029
  
  
   1065473029
  
  
   1065473029
  
 

懒汉模式

懒汉模式也称为延迟加载,就是在调用get()方法时才被创建,常见的实现办法就是在get()方法中进行new实例化。实例如下:

 
  
   public class MyObject 
  
  
       private static MyObject myObject;
  
  
       private MyObject()
  
  
    
  
  
       public static MyObject getInstance() 
  
  
           //懒汉模式
  
  
           if(myObject != null)
  
  
               //一些操作
  
  
           else 
  
  
               myObject = new MyObject();
  
  
           
  
  
           return myObject;
  
  
       
  
  
   
  
 

这种懒汉模式,若使用上一步中的多线程环境中,就可能取出多个实例,也就不符合单例模式的特点了:

 
  
   1686549717
  
  
   1204436866
  
  
   1204436866
  
 

从上边的两种单例模式来看,饿汉模式在多线程环境中是线程安全的,而对于懒汉模式来说,在多线程环境中,就是一个错误的单例模式,那么针对这个问题,该如何去处理,使得懒汉模式可以适用于多线程环境呢? 
前边的博文中介绍了使用synchronized关键字可以对代码块或者方法加锁,实现多线程的同步问题,那么我们当然也可以在单例模式的多线程环境中使用它实现线程安全。

 
  
   //声明synchronized同步方法
  
  
   public class MyObject 
  
  
       private static MyObject myObject;
  
  
       private MyObject()
  
  
    
  
  
       synchronized public static MyObject getInstance() 
  
  
           //懒汉模式
  
  
           if(myObject != null)
  
  
               //一些操作
  
  
           else 
  
  
               myObject = new MyObject();
  
  
           
  
  
           return myObject;
  
  
       
  
  
   
  
 

同步代码块

 
  
   public class MyObject 
  
  
       private static MyObject myObject;
  
  
    
  
  
       private MyObject() 
  
  
       
  
  
    
  
  
       public static MyObject getInstance() 
  
  
           try 
  
  
               //这个位置使用synchronized和方法加锁等同
  
  
               synchronized (MyObject.class) 
  
  
                   // 懒汉模式
  
  
                   if (myObject != null) 
  
  
                       // 一些操作
  
  
                    else 
  
  
                       Thread.sleep(3000);
  
  
                       myObject = new MyObject();
  
  
                   
  
  
               
  
  
            catch (InterruptedException e) 
  
  
               e.printStackTrace();
  
  
           
  
  
           return myObject;
  
  
       
  
  
   
  
 

以上这两个方式都可以实现懒汉模式在多线程环境下的线程安全,但是它们的运行效率都非常低,还需要继续修改代码进行完善。synchronized代码块同步,可以针对某些重要的代码单独进行,而其他代码则不需要,那么将上例中的同步代码块再次缩小一点,是否可以支持多线程下的懒汉模式呢?还是使用实例进行测试,如下:

 
  
   private static MyObject myObject;
  
  
   private MyObject() 
  
  
   public static MyObject getInstance() 
  
  
       try 
  
  
           // 懒汉模式
  
  
           if (myObject != null) 
  
  
               // 一些操作
  
  
            else 
  
  
               Thread.sleep(3000);
  
  
               synchronized (MyObject.class) 
  
  
               myObject = new MyObject();
  
  
                   
  
  
           
  
  
        catch (InterruptedException e) 
  
  
           e.printStackTrace();
  
  
       
  
  
       return myObject;
  
  
   
  
 

运行结果如下:

 
  
   1204436866
  
  
   789550240
  
  
   669428867
  
 

三个线程输出的hashcode都不一致,说明产生了三个不同的实例对象,那么这就是一种错误的单例模式。 
DCL双检查锁机制 
DCL(Double-checked Locking)是用来在多线程环境下懒汉模式的单例模式中避免同步开销的一个方法。针对前边的多线程中使用synchronized关键字的方式,再次进行优化,代码示例如下:

 
  
   public class MyObject 
  
  
       private volatile static MyObject myObject;  //添加volatile修饰符
  
  
       private MyObject() 
  
  
       

以上是关于java 单例模式与多线程的主要内容,如果未能解决你的问题,请参考以下文章

java 单例模式与多线程

单例模式与多线程

Java - 单例模式与多线程

单例模式与多线程

并发编程:单例与多线程

单例模式与多线程