我们先来看看下面这段代码:
public class Singleton { //利用一个静态变量来记录Singleton类的唯一实例 private static Singleton uniqueInstance; //把构造器声明为私有的,类内才能访问 private Singleton() {} //用getInstance()方法实例化对象,并返回这个实例 public static Singleton getInstance() { if(uniqueInstance == null)//为空才创建实例 { //如果我们不需要这个实例,则它永远不会被实例化。这就是“延迟实例化”。 uniqueInstance = new Singleton(); } return uniqueInstance; } }
但如果两个对象同时调用了getInstance()该怎么办呢?
我们可以采用线程同步的方法解决。
public class Singleton { //利用一个静态变量来记录Singleton类的唯一实例 private static Singleton uniqueInstance; //把构造器声明为私有的,类内才能访问 private Singleton() {} /** * 用getInstance()方法实例化对象,并返回这个实例 * 通过synchronized关键字我们可以使一个线程进入该方法前要先等待其他线程离开该方法 * 防止两个线程同时进入这个方法 */ public static synchronized Singleton getInstance() { if(uniqueInstance == null)//为空才创建实例 { //如果我们不需要这个实例,则它永远不会被实例化。这就是“延迟实例化”。 uniqueInstance = new Singleton(); } return uniqueInstance; } }
但同步会降低性能,并且我们只是第一次执行该方法时需要同步。
我们应该怎样改善多线程呢?
public class Singleton { //在静态初始化器中创建单件。这段代码保证了线程安全 private static Singleton uniqueInstance=new Singleton; //把构造器声明为私有的,类内才能访问 private Singleton() {} // 用getInstance()方法实例化对象,并返回这个实例 public static Singleton getInstance() { return uniqueInstance; } }
利用双重检查加锁,首先检查实例是否创建,未创建才进行同步。
public class Singleton { //利用一个静态变量来记录Singleton类的唯一实例 //引入volatile防止编译器对代码进行优化 //确保当变量uniqueInstance被初始化成Singleton实例时,多个线程正确的处理变量uniqueInstance private volatile static Singleton uniqueInstance; //把构造器声明为私有的,类内才能访问 private Singleton() {} /** * 用getInstance()方法实例化对象,并返回这个实例 */ public static Singleton getInstance() { if(uniqueInstance == null)//为空才同步 { synchronized(Singleton.class) { if(uniqueInstance == null)//为空才创建实例 { uniqueInstance = new Singleton(); } } } return uniqueInstance; } }
在JAVA中单利模式常分为懒汉模式和饿汉模式。
懒汉模式:
package com.cauc.singleton; /** * * * LazySingleton * 创建人:JasonZhang * 时间:2018年6月30日-下午9:24:11 * @version 1.0.0 * */ public class LazySingleton { /** * * 懒汉单利模式---运行时,编译时 * 格式: * 1:构造函数私有化 * 2:公开的方法,返回当前实例的方法(static)的 * */ //声明类的唯一实例,使用private static修饰 private static LazySingleton singleton = null; //将构造方式私有化,不允许外边直接创建对象 private LazySingleton(){ super(); } //提供一个用于获取实例的方法,使用public static修饰 public synchronized static LazySingleton getInstance(){ if(singleton==null){ singleton = new LazySingleton(); } return singleton; } }
饿汉模式:
package com.cauc.singleton; /** * * * EagerSingleton * 创建人:JasonZhang * 时间:2018年6月30日-下午9:25:34 * @version 1.0.0 * */ public class EagerSingleton { //创建类的唯一实例,使用private static修饰 private static EagerSingleton singleton = new EagerSingleton(); //将构造方法私有化,不允许外部直接创建对象 private EagerSingleton(){ super(); } //提供一个用于获取实例的方法,使用public static修饰 public static EagerSingleton getInstance(){ return singleton; } }
测试类:
package com.cauc.singleton; /** * * * Test * 创建人:JasonZhang * 时间:2018年6月30日-下午9:34:24 * @version 1.0.0 * */ public class Test { public static void main(String[] args) { //饿汉模式 EagerSingleton s1=EagerSingleton.getInstance(); EagerSingleton s2=EagerSingleton.getInstance(); if(s1==s2){ System.out.println("s1和s2是同一个实例"); }else{ System.out.println("s1和s2不是同一个实例"); } //懒汉模式 LazySingleton s3=LazySingleton.getInstance(); LazySingleton s4=LazySingleton.getInstance(); if(s3==s4){ System.out.println("s3和s4是同一个实例"); }else{ System.out.println("S3和s4不是同一个实例"); } } }