1 简介
单例模式是一种对象创建型模式。单例模式保证一个类只有一个实例存在,并且同时提供一个全局的方法进行进行访问该实例。
应用场景
-
在多个线程之间共享或对同一个对象进行操作时
-
用作全局变量时
-
大规模系统中为了提高性能,减少对象的创建,节省创建时间
2 几种实现方式
2.1 饿汉式
所谓饿汉式,就是不管三七二十一,一上来就开干。不管你需不需要创建某个对象,只要在加载类的时候就创建。
我要吃吃吃吃** - 实现方式**
实体类:Person.class
public class Person {
private String name;
private Integer age;
<!-在Person被加载的时候Person对象就被创建-!>
private static Person person = new Person();
<!--使用private ,防止用户利用空参构造器进行实例化--!>
private Person(){
}
public static Person getPerson(){
return person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
MainClass.class
public class MainClass {
public static void main(String[] args) {
Person person = Person.getPerson();
person.setAge(20);
person.setName("HAH");
System.out.println(person.getName());
}
}
在类加载时就实例化对象,这样可以保证该对象的唯一性,是线程安全的,但是相对的效率可能会降低,因为用户也许就不需要实例化该对象。
2.2 懒汉式
所谓懒汉式,顾名思义,就是用到的时候在创建。也就是说,在类加载的时候不会实例化对象,只有当用户真正的要创建对象的时候再调用方法实例化对象。
哪位湿兄可以帮我盖下被子QAQ
- 实现方式
Peison.class
public class Person {
private String name;
private Integer age;
<!--类加载时不会实例化对象--!>
private static Person person = null;
private Person(){
}
<!-用户第一次调用getPerson时会创建对象,以后都不需要创建-!>
public static Person getPerson(){
if(person == null){
person = new Person();
}
return person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
可见,使用懒汉式可以提高程序的灵活性。但是这样也存在一个很大的缺陷。当第一次调用getPerson( )
方法时有多个线程同时访问时,那么就会出现线程同步问题。所以的我们需要在方法上加上synchronized
防止多个线程同时访问。
** - 改进**
Person.class
public class Person {
private String name;
private Integer age;
private static Person person = null;
private Person(){
}
<!-在方法上加上synchronized保证当前方法同一时间点只能有一个线程访问-!>
public static synchronized Person getPerson(){
if(person == null){
person = new Person();
}
return person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
在给getPerson( )
方法上加上synchronized
可以避免多个线程第一次同时访问实例化多个对象,但是这样在方法上加上synchronized
会大大的降低程序的效率,因为只有第一次调用getPerson( )
方法时才会发生线程问题,而第二次及以后调用均不会发生线程问题,这样当有一个线程调用getPerson( )
方法时,其它线程都将被阻塞,直到调用方法的线程调用完毕。
2.3 双重检查
双重检查既是在懒汉式的改进基础上进行改进,对实例化对象的代码加锁并加以判断,从而实现线程安全并且和懒汉式相比效率也大大提高。
- 具体实现
Person.class
public class Person {
private String name;
private Integer age;
private static Person person = null;
private Person(){
}
public static Person getPerson(){
if(person == null){ (1
<!--将实例化对象的代码加锁--!>
synchronized(Person.class){ (2
<!-防止第一次调用getPerson方法时多个线程同时进入 (1,当第一线程创建完对象释放锁后,
后面的线程获得锁多次创建对象-!>
if(person == null){
person = new Person();
}
}
}
return person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
3 三种实现方式的比较
-
饿汉式和双重检查是线程安全的,而未改进的懒汉式不是线程安全的。
-
改进后的懒汉式效率比较低,因为给方法加上了
synchronized
,导致了同一时刻只能有一个线程访问该方法,造成其它线程堵塞。 -
双重检查相对于饿汉式来说灵活性更高,只有用户用到该对象的时候才会实例化,相对于懒汉式效率更高,用户只有第一次访问该,方法才会进入锁,其它线程可能会堵塞,但是以后每次方法都可以并发,线程不会再被阻塞。
最近刚学设计模式,哪理解错了,望各位湿兄大佬多多指正
网友评论