zoukankan      html  css  js  c++  java
  • Java 多线程编程中单例的实现

    对于普通单线程单例来说,较为容易,只要避免创建多个对象即可,代码如下:

    public class Singleton {
          private static Singleton singleton = null;
          public static Singleton getInstance(){
              if(singleton==null){
                       singleton = new Singleton();
              }
              return singleton;  
          }  
    }
    

    这样既可以避免创建多个对象消耗系统资源,也可以达到惰性加载,即在使用时才会创建对象;

    在单线程时,该类可以很完美的解决单例问题,但是到了多线程,该类将会出现问题:

    当两个线程A和B 同时调用单例获得实例时,若A进入getInstance()方法,判断当前singleton为空,则进入singleton=new Singeton(),并返回singleton,但是当A进入时,B同时调用,B也会判断singleton为空,因为A进入后并没有初始化完成,所以B同样会进入初始化代码,进行初始化并返回singleton,这样就出现问题了,并没有实现单例操作。

    所以在多线程环境下,我们需要对该代码进行一点修改,那就是给getInstance方法加上同步锁,使A和B不能同时进入该方法内,代码如下:

    public class Singleton {
          private static Singleton singleton = null;
          public static synchronized Singleton getInstance(){
              if(singleton==null){
                       singleton = new Singleton();
              }
              return singleton;  
          }  
    }
    

    经过修改后,我们发现在出现之前A和B 同时访问getInstance方法时,若A先进入getInstance方法,由于sychronized的存在,B是不可能进入该方法的,会在A线程执行完成之前将该方法进行加锁操作,执行完成之后才会允许B进入该方法,而当B进入方法时,singleton已经不再是null,直接返回单例singleton对象;

    但是实际上,若多个线程调用时,同步锁的存在,很大程度上影响程序性能,所以后来有人提出double-checked blocking方法,来降低性能影响:

    还有一种方法,叫做double-check blocking,代码如下:

    public class Singleton {
          private static Singleton singleton = null;
          public static Singleton getInstance(){
              if(singleton==null){
                     synchronized (singleton){
                          if(singleton==null){
                               singleton = new Singleton();       
                            }
                      }      
              }
              return singleton;  
          }  
    }                
    

    经过此次修改,当线程进入getInstance方法后,将会先判断singleton是否为空,这样减少了进入同步块所花费的资源,降低了资源消耗,提高了性能,

    似乎这样修改以后,即不会造成同步锁消耗资源,也不会由于多线程同时进入创建多个对象,但是,实际上,从更深一层来讲,站在jvm的层面来说,这样的代码仍可能发生问题:

    由于在执行singleton = new Singleton();时,jvm是分两步进行的,先是为singleton预留空间,直接赋值给instance,然后才会初始化singleton,创建singleton实例,

    如果A先进入singleton = new sSingleton(),但是只是分配了空间,并没有初始化完成,就返回singleton,那么当B线程进入时,singleton已经不为null,那么将直接返回已有的singleton,若在singleton真正初始化之前就使用的话,问题就来了,所有 多线程单例模式,又出现了新的解决办法:

    public class Singleton{
         private static c lass SingletonContainer{
                 private static Singleton instance = new Singleton();
         }
         public static Singleton getInstance(){
                return SingletonContainer.instance;
         }
    
    }
    

      该方法为通过内部类实现多线程环境中的单例,

    JVM内部机制能够保证当一个类被加载时,这个类的加载过程是线程安全的,当我们第一次调用getInstance方法时,JVM能够保证instance只被创建一次,并且保证初始化完成,这样我们就不再需要担心instance没有被创建完成了,同时实现了惰性加载单例

  • 相关阅读:
    网管必备网站地址
    数组是否包含某个元素
    Thinking in java(八)
    Thinking in java(八)
    Java8系列之重新认识HashMap
    Java8系列之重新认识HashMap
    MarkdownPad2.5/2 注册码
    MarkdownPad2.5/2 注册码
    java8函数式编程(2)
    java8函数式编程(2)
  • 原文地址:https://www.cnblogs.com/mecca/p/4483525.html
Copyright © 2011-2022 走看看