单例模式(singleton)
动机:保证类在系统中只存在一个实例
例:配置文件
public class AppConfig
{
private String parameterA;
private String parameterB;
public String getParameterA() {
return parameterA;
}
public String getParameterB() {
return parameterB;
}
public AppConfig()
{
readConfig();
}
private void readConfig() {
// TODO Auto-generated method stub
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();
}
}
}
}
public class main
{
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
AppConfig config=new AppConfig();
String paramA=config.getParameterA();
String paramB=config.getParameterB();
System.out.println("paramA="+paramA+",paramB="+paramB);
}
}
问题:系统运行时,系统会存在很多AppConfig的实例对象,如果配置文件多大,会造成很大的资源浪费。
解决方法:单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。创建一个唯一地变量对象。
class Singleton
{
private static Singleton instance;
private Singleton()
{
//构造方法为private,外界就不能利用new创建此类型的实例
}
public static Singleton GetInstance()
{//获得本类实例的唯一全局访问点
if(instance==null)
{
//如果实例不存在则new一个新实例,否则返回已有的实例
instance=new Singleton();
}
return instance;
}
}
public class main
{
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
/*Singleton s0=new Singleton()*/
//错误,外界不能通过new创建此类型
Singleton s1=Singleton.GetInstance();
Singleton s2=Singleton.GetInstance();
if(s1==s2)//判断两次实例化后对象的结果是实例相同
{
System.out.println("两个对象是相同的实例");
}
else
{
System.out.println("两个对象是不同的实例");
}
}
}
单例模式特点:
- 只能有一个实例
- 必须自己创建自己的唯一实例
- 必须给所有其他对象提供这一实例
多线程时的单例
多线程的程序,多个线程同时访问Singleton类,调用GetInstance()方法,有可能造成创建多个实例。
解决:在执行new创建实例的地方加上锁,同时在锁定之前判断下是否为null,这样如果已经创建就不用进入锁了。
java使用synchronized上锁
class Singleton
{
private static Singleton instance;
private static final Object syncRoot=new Object();
//程序运行时创建一个静态只读的进程辅助对象
private Singleton()
{
//构造方法为private,外界就不能利用new创建此类型的实例
}
public static Singleton GetInstance()
{//获得本类实例的唯一全局访问点
synchronized(syncRoot)//在同一个时刻加了锁的部分程序只有一个线程可以进入
{
if(instance==null)
{
//如果实例不存在则new一个新实例,否则返回已有的实例
instance=new Singleton();
}//不直接synchronized(instance),因为若instance为空,则无法对它加锁
}
System.out.println("singleton创建");
return instance;
}
}//使得对象实例由最先进入的那个线程创建,以后的线程在进入时不会再去创建对象实例了。由于
//synchronized保证了多线程环境下的同时访问也不会造成多个实例的生成。
public class main
{
public static void main(String[] args) {
Singleton s1=Singleton.GetInstance();
Singleton s2=Singleton.GetInstance();
if(s1==s2)//判断两次实例化后对象的结果是实例相同
{
System.out.println("该对象的字符串表示形式:");
System.out.println("s1:"+s1.toString());
System.out.println("s2:"+s2.toString());
}
}
}
结果为:
singleton创建
singleton创建
该对象的字符串表示形式:
s1:text.Singleton@52cc8049
s2:text.Singleton@52cc8049
//包名.对象名+@+十六进制的对象的哈希值(JVM虚拟出来的对象内存地址)
在每次调用GetInstance方法时都需要同步,降低了性能
解决:双重锁定
双重锁定
class SingletonDCL
{
private volatile static SingletonDCL singleton;
private SingletonDCL()
{
}
public static SingletonDCL GetInstance()
{
if(singleton==null)
{//各类加锁,类的所有对象用同一把锁
synchronized(SingletonDCL.class)
{
if(singleton==null)//多线程进入,防止第二个线程创建实例,破坏单例原则
{
singleton=new SingletonDCL();
}
}
}
System.out.println("singleton创建");
return singleton;
}
}
public class main
{
public static void main(String[] args) {
SingletonDCL s1=SingletonDCL.GetInstance();
SingletonDCL s2=SingletonDCL.GetInstance();
System.out.println("s1==s2?"+(s1==s2));
System.out.println("该对象的字符串表示形式:");
System.out.println("s1:"+s1.toString());
System.out.println("s2:"+s2.toString());
}
}
结果同样为:
singleton创建
singleton创建
s1==s2?true
该对象的字符串表示形式:
s1:text.SingletonDCL@52cc8049
s2:text.SingletonDCL@52cc8049
单例模式分类
- 懒汉式(时间换时间):面临多线程的安全问题需要双重锁定
- 饿汉式(空间换时间):即静态初始化的方法,类一加载就实例化,所有要提前占用系统资源
使用单例模式的一个必要条件:在一个系统要求一个类只有一个实例