【设计模式】单例模式的实现--------java

单例模式:
一个类只有一个实例,并提供一个访问它的全局访问点。
应用场景:创建一个唯一的变量(对象)。
1.场景问题:(读取配置文件到内存)
文件名:AppConfig.properties
内容:paramA=a
paramB=b

package computer;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class Test {
	public static void main(String[] args) {
		AppConfig config=new AppConfig();
		String paramA=config.getParameterA();
		String paramB=config.getParameterB();
		
		System.out.println("paramA="+paramA+"paramB="+paramB);
	}
}

class AppConfig{
	private String parameterA;
	private String parameterB;
	
	public AppConfig(){
		readConfig();
	}
	public String getParameterA() {
		return parameterA;
	}
	public String getParameterB() {
		return parameterB;
	}
	
	private void readConfig() {
		Properties p=new Properties();
		InputStream in=null;
		try {
			in=AppConfig.class.getResourceAsStream("AppConfig.properties");
			p.load(in);
			this.parameterA=p.getProperty("paramA");
			this.parameterB=p.getProperty("paramB");
		}catch(IOException e) {
			System.out.println("装载配置文件出错!");
			e.printStackTrace();
		}finally{
			try {
				in.close();
			}catch(IOException e) {
				e.printStackTrace();
			}
		}
	}
	
}

问题:系统运行时,多处要用到配置文件,即系统会创建多个AppConfig的实例对象,资源浪费。

2.解决方案:(单例模式)
【设计模式】单例模式的实现--------java_第1张图片
构造函数私有,用户无法通过new直接实例;静态方法负责检验并实例化自己,确保只有一个实例被创建。
注意:类中的私有成员变量也是静态的!

package computer;

public class Test {
	public static void main(String[] args) {
		Singleton s1=Singleton.GetInstance();
		Singleton s2=Singleton.GetInstance();
		
		if(s1==s2) {
			System.out.println("两个对象是相同的实例!");
		}else {
			System.out.println("两个对象是不同的实例!");
		}
	}
}

class Singleton{
	private static Singleton instance;
	//构造方法为私有,外界无法用new创建此类的对象
	private Singleton() {}
	
	public static Singleton GetInstance() {
		//实例不存在就new一个新实例,存在就返回已经有的实例
		if(instance==null) {
			instance=new Singleton();
		}
		return instance;
	}
}

缺点:多线程时可能同时有两个对象被创建。

3.多线程时的单例模式:
用到synchronized():被synchronized()修饰的区域每次只有一个线程可访问,其他线程若试图进入锁定的代码,将被阻止,直到对象被释放。

package computer;

public class Test {
	public static void main(String[] args) {
		Singleton s1=Singleton.GetInstance();
		Singleton s2=Singleton.GetInstance();
		
		if(s1==s2) {
			System.out.println("两个对象是相同的实例!");
			System.out.println("singleton1:"+s1.toString());
			System.out.println("singleton2:"+s2.toString());
		}else {
			System.out.println("两个对象是不同的实例!");
			System.out.println("singleton1:"+s1.toString());
			System.out.println("singleton2:"+s2.toString());
		}
	}
}

class Singleton{
	private static Singleton instance;
	private static final Object syncRoot=new Object();
	private Singleton() {}
	
	public static Singleton GetInstance() {
		//在同一时刻加了锁的部分程序只有一个线程可以进入(即第一个线程进入以后,锁住。其他线程再也进不来,除非该对象被释放。)
		synchronized(syncRoot) {
			if(instance==null) {
				instance=new Singleton();
			}
		}
		System.out.println("singleton创建。");
		return instance;
	}
}

解释:不直接synchronized(instance),而是再创建一个syncRoot来synchronized的原因是:当instance还没有被创建过时,因为不存在instance,就没法锁instance。
缺点:每次调用getInstance()方法时都需要同步,降低了性能。
解决方法:双重锁定。

4.双重锁定的单例模式:
不用让线程每次都加锁,而只是在实例未被创建的时候加锁。

package computer;

public class Test {
	public static void main(String[] args) {
		SingletonDCL s1=SingletonDCL.getInstance();
		SingletonDCL s2=SingletonDCL.getInstance();
		
		if(s1==s2) {
			System.out.println("两个对象是相同的实例!");
			System.out.println("singleton1:"+s1.toString());
			System.out.println("singleton2:"+s2.toString());
		}else {
			System.out.println("两个对象是不同的实例!");
			System.out.println("singleton1:"+s1.toString());
			System.out.println("singleton2:"+s2.toString());
		}
	}
}

class SingletonDCL{
	//Volatile:当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。
	private volatile static SingletonDCL instance;
	private SingletonDCL() {}
	
	//此方法是获得本类实例的唯一全局访问点:
	public static SingletonDCL getInstance() {
		if(instance==null) {
			//给类加锁,类的所有对象同用一把锁:
			synchronized(SingletonDCL.class){
				if(instance==null) {
					instance=new SingletonDCL();
				}
			}
		}
		System.out.println("singleton创建!");
		return instance;
	}
}

解释:为什么要在判断了实例是否存在后再次判断实例是否存在?因为在instance为null的情况下可能会有同时两个线程同时执行getInstance()方法,则他们都可以通过instance==null的判断,然后,由于synchronized(SingletonDCL.class),这两个线程只有一个可以进入,另一个排队等第一个出来以后再进入。这是如果没有第二重instence是否为null的判断,则第一个线程创建实例后,第二个线程还可以创建实例。

5.静态内部类:(没讲)
定义静态内部类SingletonHolder,Singleton被装载时,instance不被初始化,因为SingletonHolder类没有被主动使用,只有显式调用getInstance()方法时,才会显式装载SingletonHolder类,从而实例化instance。

package computer;

public class Test {
	public static void main(String[] args) {
		Singleton s1=Singleton.getInstance();
		Singleton s2=Singleton.getInstance();
		
		if(s1==s2) {
			System.out.println("两个对象是相同的实例!");
			System.out.println("singleton1:"+s1.toString());
			System.out.println("singleton2:"+s2.toString());
		}else {
			System.out.println("两个对象是不同的实例!");
			System.out.println("singleton1:"+s1.toString());
			System.out.println("singleton2:"+s2.toString());
		}
	}
}

class Singleton{
	private static class SingletonHolder{
		private static final Singleton INSTANCE=new Singleton();
	}
	
	private Singleton() {}
	public static final Singleton getInstance() {
		return SingletonHolder.INSTANCE;
	}
}

6.单例模式分类:
1)懒汉单例模式(时间换空间):在第一次被引用时才会将自己实例化。
缺点:面临多线程访问的
安全问题。

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

2)饿汉单例模式(空间换时间):在静态初始化期间或者在类构造函数中分配变量。在自己被加载时就将自己实例化。
缺点:提前占用系统资源。

class Singleton{
	//在类加载的时候被创建,是static所以后期不会再改变,所以是线程安全的:
	private static Singleton instance=new Singleton();
	private Singleton() {}
	
	public static Singleton getInstance() {
		return instance;
	}
}

7.改写开头例子:
1)懒汉单例模式:

package computer;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class Test {
	public static void main(String[] args) {
		AppConfig config=AppConfig.getAppConfig();
		String paramA=config.getParameterA();
		String paramB=config.getParameterB();
		
		System.out.println("paramA="+paramA+"paramB="+paramB);
	}
}

class AppConfig{
	private String parameterA;
	private String parameterB;
	
	//增加该类静态成员:
	private static AppConfig appconfig;
	//构造方法改为私有:
	private AppConfig(){
		readConfig();
	}
	
	public String getParameterA() {
		return parameterA;
	}
	public String getParameterB() {
		return parameterB;
	}
	
	private void readConfig() {
		Properties p=new Properties();
		InputStream in=null;
		try {
			in=AppConfig.class.getResourceAsStream("AppConfig.properties");
			p.load(in);
			this.parameterA=p.getProperty("paramA");
			this.parameterB=p.getProperty("paramB");
		}catch(IOException e) {
			System.out.println("装载配置文件出错!");
			e.printStackTrace();
		}finally{
			try {
				in.close();
			}catch(IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	//添加静态getAppConfig()方法
	public static AppConfig getAppConfig() {
		if(appconfig==null) {
			appconfig=new AppConfig();
		}
		return appconfig;
	}
}

2)多线程的懒汉单例模式:

package computer;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class Test {
	public static void main(String[] args) {
		AppConfig config=AppConfig.getAppConfig();
		String paramA=config.getParameterA();
		String paramB=config.getParameterB();
		
		System.out.println("paramA="+paramA+"paramB="+paramB);
	}
}

class AppConfig{
	private String parameterA;
	private String parameterB;
	
	//增加该类静态成员:
	private static AppConfig appconfig;
	//增加
	private static final Object syncRoot=new Object();
	//构造方法改为私有:
	private AppConfig(){
		readConfig();
	}
	
	public String getParameterA() {
		return parameterA;
	}
	public String getParameterB() {
		return parameterB;
	}
	
	private void readConfig() {
		Properties p=new Properties();
		InputStream in=null;
		try {
			in=AppConfig.class.getResourceAsStream("AppConfig.properties");
			p.load(in);
			this.parameterA=p.getProperty("paramA");
			this.parameterB=p.getProperty("paramB");
		}catch(IOException e) {
			System.out.println("装载配置文件出错!");
			e.printStackTrace();
		}finally{
			try {
				in.close();
			}catch(IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	//添加静态getAppConfig()方法
	public static AppConfig getAppConfig() {
		synchronized(syncRoot) {
			if(appconfig==null) {
				appconfig=new AppConfig();
			}
		}
		return appconfig;
	}
}

3)双重锁定的懒汉模式:

package computer;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class Test {
	public static void main(String[] args) {
		AppConfig config=AppConfig.getAppConfig();
		String paramA=config.getParameterA();
		String paramB=config.getParameterB();
		
		System.out.println("paramA="+paramA+"paramB="+paramB);
	}
}

class AppConfig{
	private String parameterA;
	private String parameterB;
	
	//增加该类静态成员:
	private static AppConfig appconfig;
	//构造方法改为私有:
	private AppConfig(){
		readConfig();
	}
	
	public String getParameterA() {
		return parameterA;
	}
	public String getParameterB() {
		return parameterB;
	}
	
	private void readConfig() {
		Properties p=new Properties();
		InputStream in=null;
		try {
			in=AppConfig.class.getResourceAsStream("AppConfig.properties");
			p.load(in);
			this.parameterA=p.getProperty("paramA");
			this.parameterB=p.getProperty("paramB");
		}catch(IOException e) {
			System.out.println("装载配置文件出错!");
			e.printStackTrace();
		}finally{
			try {
				in.close();
			}catch(IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	//添加静态getAppConfig()方法
	public static AppConfig getAppConfig() {
		if(appconfig==null) {
			synchronized(AppConfig.class) {
				if(appconfig==null) {
					appconfig=new AppConfig();
				}
			}
		}
		return appconfig;
	}
}

4)饿汉单例模式:

package computer;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class Test {
	public static void main(String[] args) {
		AppConfig config=AppConfig.getAppConfig();
		String paramA=config.getParameterA();
		String paramB=config.getParameterB();
		
		System.out.println("paramA="+paramA+"paramB="+paramB);
	}
}

class AppConfig{
	private String parameterA;
	private String parameterB;
	
	//添加该类的静态私有成员:
	private static AppConfig appconfig=new AppConfig();
	
	//修改构造函数为私有:
	private AppConfig(){
		readConfig();
	}
	public String getParameterA() {
		return parameterA;
	}
	public String getParameterB() {
		return parameterB;
	}
	
	private void readConfig() {
		Properties p=new Properties();
		InputStream in=null;
		try {
			in=AppConfig.class.getResourceAsStream("AppConfig.properties");
			p.load(in);
			this.parameterA=p.getProperty("paramA");
			this.parameterB=p.getProperty("paramB");
		}catch(IOException e) {
			System.out.println("装载配置文件出错!");
			e.printStackTrace();
		}finally{
			try {
				in.close();
			}catch(IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	//添加静态getAppConfig()方法
	public static AppConfig getAppConfig() {
		return appconfig;
	}
}

你可能感兴趣的:(设计模式,java,笔记,设计模式,java)