单例模式(Singleton)概念,类别,代码实现

1.什么是单例模式?

单例模式是是一种常用的软件设计模式,最简单的设计模式之一。
单例模式是一种对象创建型模式,使用单例模式可以保证一个类只生成唯一的实例对象。即在整个程序空间中,该类只产生一个实例对象
应用场景:

  • 一些资源管理器常常设计成单例模式。

  • 在多个线程之间,比如servlet中,共享同一个资源或操作同一个对象。

  • 在整个程序空间使用全局变量,共享资源

  • 大规模系统中,为了性能的考虑,需要节省对象的创建时间等等。

因为单例模式保证一个类只产生一个实例,所以这些情况下,单例模式就派上用场了。

2.有哪几种形式?

第一种形式:饿汉式

单例类
/**
 * 饿汉式 单例模式
 * 可以在多线程中保证,线程安全
 */
public class Person1 {
	private String name;
	public static Person1 person = new Person1();

	//将构造方法私有化,这样在其他类中无法使用
	private Person1(){
	}
	
	//提供一个全局的静态方法
	public static Person1 getPerson(){
		return person;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}

测试类
public class Test {
	public static void main(String[] args) {
		Person1 p1 = Person1.getPerson();//通过静态方法来创建对象实例
		p1.setName("李四");
		System.out.println(p1.getName());
		
		Person1 p2 = Person1.getPerson();
		System.out.println(p2 == p1);//true 说明p1和p2的地址相同,同一个对象
		System.out.println(p2.getName());//李四  p2并没有赋值,但是name仍然是李四
		p2.setName("张三");
		System.out.println(p1.getName());
	}
}

饿汉式每次调用的时候不用做创建,直接返回已经创建好的实例。这样虽然节省了时间,但是却占用了空间,实例本身为static的,会一直在内存中带着。因为饿汉单例类在类的初始化时,已经自行实例化,所以线程安全。

第二种形式:懒汉式,也是常用的形式。

/**
 * 懒汉式
 * 只能在单线程中保证
 */
public class Person2 {
	private String name;
	private static Person2 person;

	//将构造方法私有化,这样在其他类中无法使用
	private Person2(){
	}
	
	//提供一个全局的静态方法
	public static Person2 getPerson(){
		if(person == null){
			person = new Person2(); 
		}
		return person;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

顾名思义,lazy loading(延迟加载,一说懒加载),在需要的时候才创建单例对象,而不是随着软件系统的运行或者当类被加载器加载的时候就创建。当单例类的创建或者单例对象的存在会消耗比较多的资源,常常采用lazy loading策略。这样做的一个明显好处是提高了软件系统的效率,节约内存资源。

当多个线程并发时,可能会有几个线程同时进入getPerson()的if语句中,然后分别创建不同的person对象,这就不能保证只有唯一一个对象,所以懒汉式线程不安全,只有单线程时是安全的。
通过使用同步方法(synchronized)修改getPerson()可以使程序符合单例模式要求:

public static synchronized Person2 getPerson(){
		if(person == null){
			person = new Person2(); 
		}
		return person;
}
	

当某个线程执行同步方法时(synchronized),其他线程不能执行该方法,即独占,从而保证只有一个实例。

但是这样的话,会带来新的问题。当某个线程在执行该方法时,其他线程就会等待,效率较低。改进的方法是:只需要把需要同步的语句同步,这样就引出了第三种形式的单例模式。

第三种形式:双重检查

/**
 * 双重检查
 * 效率高
 */
public class Person4 {
	private String name;
	private static Person4 person;

	//将构造方法私有化,这样在其他类中无法使用
	private Person4(){
	}
	
	//改进:只需要把需要同步的语句同步
	//但是可能有多个线程进入第一个if,所以需要第二次判断
	public static Person4 getPerson(){
		if(person == null){
			synchronized (Person4.class) {
				if(person == null){
					person = new Person4(); 
				}
			}		
		}
		return person;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

这种经过改良的单例模式是线程安全的。

效率提高的原因?将同步内容移动到if内部,提高了执行的效率,不必每次获取对象时都进行同步,只有第一次才同步,创建了以后就没必要了。如果双重if判断,100的线程可以同时if判断,理论消耗的时间只有一个if判断的时间。

为什么需要第二次判断?多个线程并发时,可能有多个线程进入第一个if,所以需要第二次判断。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值