设计模式之单例模式

        我们初始学面向对象编程的时候,都是用new开始生成对象,哪里需要对象的时候直接一个new。即使几次传入构造函数的参数都是相同的,其实两个对象是不同的(当然重写equal()函数和hashcode()函数的那是例外),这样便会造成对象满天飞的情况,这样相当于栈里面的一个对象引用名都会指向一个堆里面的一个对象,这样导致堆里面出现过多重复的对象,会使性能下降。这时候,如果使用了单例模式,在相应类中声明一个静态类变量,初始值为null,如果该值不为null,则直接返回该变量,否则生成一个对象赋予静态变量。这样整个过程中,只需要生成一次对象。Spring容器中就有很明显的单例模式,比如Bean的作用域默认就是singleton,属于单例模式。

        接下来,我们通过举例对比的形式,来讲解单例模式的特点。首先先举个最普通的例子。

package singletonpattern;

class Simple
{
	public Simple(){}
	public static Simple getSimple()
	{
		return new Simple();
	}
}
public class General {
	public static void main(String[] args)
	{
		Simple s1=new Simple();
		Simple s2=new Simple();
		Simple s3=Simple.getSimple();
		Simple s4=Simple.getSimple();
		System.out.println(s1==s2);
		System.out.println(s3==s4);
	}

}
        以上就是没有使用单例模式的,通过普通的方式创建对象,运行程序,得到如下结果:

false
false
        可以看出使用普通的创建对象的方式即使输入参数相同得到的对象也不相等。接下来,我们看看单例模式的例子,如下所示:

package singletonpattern;

class Singleton
{
	private static Singleton instance;
	private Singleton() {}
	public static Singleton getInstance()
	{
		if(instance==null)
		{
			instance=new Singleton();
		}
		return instance;
	}
}
public class SingletonTest {
	public static void main(String[] args)
	{
		Singleton s1=Singleton.getInstance();
		Singleton s2=Singleton.getInstance();
		System.out.println(s1==s2);
	}

}
        运行程序得到如下结果:

true
        看到上面的结果,我们可以知道单例模式的优点,就是对于每个对象,只需建立第一个对象,后面不需要生成对象,用的都是第一个对象,这样看可以降低系统开销,尤其是一些需要大量使用重复对象的场景。
        接下来,引入两个并发编程中的两个关键字:volatile和synchronized。没错,我们要讲一下多线程中的单例模式,synchronized关键字具有保证线程安全的作用,下面这段代码:


public class ChocolateBoiler {
	private boolean empty;
	private boolean boiled;
	private static ChocolateBoiler chocolateboiler;
	
	private ChocolateBoiler()
	{}
	
	public static synchronized ChocolateBoiler getInstance()
	{
		if(chocolateboiler==null)
		{
			
				if(chocolateboiler==null)
				{
					chocolateboiler=new ChocolateBoiler();
				}
			
		}
		return chocolateboiler;
	}
	
	public void fill()
	{
		if(isEmpty())
		{
			empty=false;
			boiled=false;
		}
	}
	public void drain()
	{
		if(!isEmpty()&&isBoiled())
		{
			empty=true;
		}
	}
	public void boil()
	{
		if(!isEmpty()&&!isBoiled())
		{
			boiled=true;
		}
	}
	public boolean isEmpty()
	{
		return empty;
	}
	public boolean isBoiled()
	{
		return boiled;
	}

}
上面这段代码是指制作巧克力过程,利用synchronized修饰方法,可以保证每个线程在进入这个方法之前,要先等别的线程离开这个方法,也就是不会有两个线程同时进入这个方法。但是这个方法也存在问题,就是在刚执行这个方法的时候需要同步,需要加热然后再加原料,则这个创建对象就是一个加热过程,所以,之后再执行该方法就是多余的,还会降低性能。这时候我们引入volatile关键字,如以下代码所示:

package singletonpattern;

public class ChocolateBoiler {
	private boolean empty;
	private boolean boiled;
	private static volatile ChocolateBoiler chocolateboiler;
	
	private ChocolateBoiler()
	{}
	
	public static ChocolateBoiler getInstance()
	{
		if(chocolateboiler==null)
		{
			synchronized(ChocolateBoiler.class){
				if(chocolateboiler==null)
				{
					chocolateboiler=new ChocolateBoiler();
				}
			}
			
		}
		return chocolateboiler;
	}
	
	public void fill()
	{
		if(isEmpty())
		{
			empty=false;
			boiled=false;
		}
	}
	public void drain()
	{
		if(!isEmpty()&&isBoiled())
		{
			empty=true;
		}
	}
	public void boil()
	{
		if(!isEmpty()&&!isBoiled())
		{
			boiled=true;
		}
	}
	public boolean isEmpty()
	{
		return empty;
	}
	public boolean isBoiled()
	{
		return boiled;
	}

}
volatile关键字确保当其修饰的变量被初始化成实例,多个线程可以正确地被实例变量处理;和synchronized组成的双重检查锁,首先检查是否实例已经创建了,如果未创建才进行同步,所以只有第一次会同步,这么做改善了性能。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值