设计模式原理及大数据实践之一:手写一个单例模式

文章目录

    • 单例模式的实现方法
    • 手写一个单例模式
    • 怎么样控制只能实例化一次?
    • 多线程时的单例
      • 饿汉式单例
      • 懒汉式单例
    • 对象的产生方式
    • 单例模式应用
      • SparkContext
      • SparkStreaming

单例模式的实现方法

单例模式的目的是只生产一个对象实例,所有依赖它的对象访问到的都是同一个实例。通过将构造函数私有化,并提供一个public静态方法提供此实例。

手写一个单例模式

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

怎么样控制只能实例化一次?

对于外部代码,不能用new来实例化,因此把构造函数声明为私有。
实例化只能由类本身来构造,并对外提供一个getInstance()方法。每次调用会判断是否实例化过,然后提供实例化过的对象。

多线程时的单例

上述代码在高并发环境下不安全,可能会产生多个实例。多线程下可以使用以下方法。

饿汉式单例

public class Singleton{
	private static final Singleton instance = new Singleton();
	private Singleton(){
	}
	public static Singleton getInstance(){
		return instance;
	}
}

懒汉式单例

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

或者

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

注:对象复制式不调用类的构造函数的,因此单例模式不要实现Cloneable接口。

对象的产生方式

通过new关键字、复制、反射。

单例模式应用

单例模式只生成一个对象实例,并且不用反复创建销毁,减少了系统的性能开销。由于常驻内存,JAVA中使用要注意JVM垃圾回收。
在整个程序中,要提供一个共享访问点或共享数据,会用到单例模式。以下举例说明:

SparkContext

大数据计算框架Spark启动后,会初始化SparkContext(sc)。SparkContext通过getOrCreate函数,保证一个JVM只启动一个SparkContext。

class SparkContext(config: SparkConf) extends Logging {
  private val allowMultipleContexts: Boolean = config.getBoolean("spark.driver.allowMultipleContexts", false)
  ...
}

object SparkContext extends Logging {
 /**
   * Lock that guards access to global variables that track SparkContext construction.
   */
  private val SPARK_CONTEXT_CONSTRUCTOR_LOCK = new Object()

  /**
   * The active, fully-constructed SparkContext.  If no SparkContext is active, then this is `null`.
   *
   * Access to this field is guarded by SPARK_CONTEXT_CONSTRUCTOR_LOCK.
   */
  private val activeContext: AtomicReference[SparkContext] =
    new AtomicReference[SparkContext](null)
/**
   * This function may be used to get or instantiate a SparkContext and register it as a singleton object. Because we can only have one active SparkContext per JVM, this is useful when applications may wish to share a SparkContext.
   * @note This function cannot be used to create multiple SparkContext instances even if multiple contexts are allowed.
   * @param config `SparkConfig` that will be used for initialisation of the `SparkContext`
   * @return current `SparkContext` (or a new one if it wasn't created before the function call)
   */
  def getOrCreate(config: SparkConf): SparkContext = {
    // Synchronize to ensure that multiple create requests don't trigger an exception from assertNoOtherContextIsRunning within setActiveContext
    SPARK_CONTEXT_CONSTRUCTOR_LOCK.synchronized {
      if (activeContext.get() == null) {
        setActiveContext(new SparkContext(config), allowMultipleContexts = false)
      } else {
        if (config.getAll.nonEmpty) {
          logWarning("Using an existing SparkContext; some configuration may not take effect.")
        }
      }
      activeContext.get()
    }
  }
}

通过SPARK_CONTEXT_CONSTRUCTOR_LOCK.synchronized锁机制实现了多线程的懒汉式单例模式。

SparkStreaming

在Spark Streaming中,如果启用了checkpoint,并且使用了累加器或者广播变量,累加器或者广播变量将不能从checkpoint恢复。如果要使用,需要使用单例模式,才能在driver重启后恢复。
累加器Scala代码如下:

object DroppedWordsCounter {
  @volatile private var instance: Accumulator[Long] = null
  def getInstance(sc: SparkContext): Accumulator[Long] = {
    if (instance == null) {
      synchronized {
        if (instance == null) {
          instance = sc.accumulator(0L, "WordsInBlacklistCounter")
        }
      }
    }
    instance
  }
}

广播变量Scala代码如下:

object WordBlacklist {
  @volatile private var instance: Broadcast[Seq[String]] = null
  def getInstance(sc: SparkContext): Broadcast[Seq[String]] = {
    if (instance == null) {
      synchronized {
        if (instance == null) {
          val wordBlacklist = Seq("a", "b", "c")
          instance = sc.broadcast(wordBlacklist)
        }
      }
    }
    instance
  }
}

参考资料:
《设计模式之禅(第2版)》(秦小波 著)
《大话设计模式》(程杰 著)
https://spark.apache.org/docs/1.6.1/streaming-programming-guide.html#accumulators-and-broadcast-variables

你可能感兴趣的:(设计模式)