Java 单例模式
桃花键神 人气:0专栏介绍
【JAVA长虹键法】 主要讲了23种设计模式,本系列专栏会以虹猫蓝兔七侠传的故事为例来给大家详细分析所有模式,希望能给大家带来帮助!
本期介绍
模式: 单例模式
案例: 虹猫蓝兔造剑
什么是单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例模式大致分为懒汉式和饿汉式,接下来用案例分析
懒汉式一
是否 Lazy 初始化: 是
是否多线程安全:否
实现难度: 易
描述: 这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
概念看不懂没关系,接下来举例。
案例一:
创建一个剑类,这个类可以实例化一把剑。
虹猫和蓝兔两个人都想要造一把剑,虹猫先打造了一把剑,命名为长虹剑,然后蓝兔也打造了一把剑,但是没有命名。
现在来分析两个情况,一个情况是正常模式,另一种情况是单例模式。
正常模式
剑类:
public class Jians { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
测试类:
public class Demo1 { public static void main(String[] args) { //虹猫的剑 Jians hong = new Jians(); //蓝兔的剑 Jians lan = new Jians(); hong.setName("长虹剑"); System.out.println(hong.getName()); System.out.println(lan.getName()); } }
结果:
在正常模式下,我new了虹猫和蓝兔的剑。其实就是两把剑,两个不同的对象。
单例模式
剑类:
剑类中的getInstance()方法有一个造剑的功能,也就是new一个剑对象的功能。
public class Jian { private static Jian jian; private String name; public static Jian getInstance() { if (jian == null) { jian = new Jian(); } return jian; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
测试类:
public class Demo { public static void main(String[] args) { //虹猫的剑 Jian hong = Jian.getInstance(); //蓝兔的剑 Jian lan = Jian.getInstance(); //虹猫把剑命名长虹剑 hong.setName("长虹剑"); //输出 System.out.println(hong.getName()); System.out.println(lan.getName()); } }
结果:
在单例模式下,我new了虹猫和蓝兔的剑。其实就是一把剑,一个相同的对象。
为什么线程不安全呢
单例模式下的剑类:
public class Jian { private static Jian jian; private String name; public static Jian getInstance() { if (jian == null) { jian = new Jian(); } return jian; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
就拿这个类来说,他是线程不安全的。因为他通过getInstance()方法来获取对象。如果是多线程运行,有线程1和线程2都先后进入了这个方法,因为线程1刚进入方法还没有返回对象,线程2就进入了方法。所以线程2也会new一个对象,因为此时线程2进入方法的时候jian还是null的。
懒汉式二
是否 Lazy 初始化: 是
是否多线程安全: 是
实现难度: 易
描述: 这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点: 第一次调用才初始化,避免内存浪费。
缺点: 必须加锁 synchronized 才能保证单例,但加锁会影响效率。
为什么线程安全呢
这里还用上面那个案例,这次主要介绍懒汉式一和懒汉式二的区别。
懒汉式一和懒汉式二的主要区别在剑类上。
懒汉式一的剑类:
public class Jian { private static Jian jian; private String name; public static Jian getInstance() { if (jian == null) { jian = new Jian(); } return jian; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
懒汉式二的剑类:
public class Jian { private static Jian jian; private String name; public static synchronized Jian getInstance() { if (jian == null) { jian = new Jian(); } return jian; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
在getInstance()方法前加了个字段synchronized,synchronized是一把锁,作用是同一时间只能有一个线程进入这个方法,这样就避免了懒汉式一中两个线程都进入方法的情况出现,就不会new两个对象,所以线程安全。
饿汉式
是否 Lazy 初始化: 否
是否多线程安全: 是
实现难度: 易
描述: 这种方式比较常用,但容易产生垃圾对象。
优点: 没有加锁,执行效率会提高。
缺点: 类加载时就初始化,浪费内存。
饿汉式就是直接在类中new一个对象,就是不管你需不需要剑我已经把剑造好了。
饿汉式剑类:
public class Jian { private static Jian jian=new Jian(); private String name; public static synchronized Jian getInstance() { return jian; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
测试类:
public class Demo { public static void main(String[] args) { //虹猫的剑 Jian hong = Jian.getInstance(); //蓝兔的剑 Jian lan = Jian.getInstance(); //虹猫把剑命名长虹剑 hong.setName("长虹剑"); //输出 System.out.println(hong.getName()); System.out.println(lan.getName()); } }
饿汉式中剑类的getInstance()方法已经失去了造剑的功能,测试类调用它只是返回一把提前造好的剑
懒汉式与饿汉式的区别
还是这个案例,虹猫和蓝兔都想造一把剑,懒汉式是有一个剑类,剑类中有一个造剑的方法,调用这个方法的时候打造一把剑。饿汉式也有一个剑类,不同的是这个剑类直接就把剑造好了,没有造剑的方法,不管你虹猫和蓝兔想不想造剑,剑都已经造好了。
优点: 没有加锁,执行效率会提高。
缺点: 类加载时就初始化,浪费内存。
现在在看饿汉式的优缺点就容易理解了。
下期预告
模式: 简单工厂模式
案例: 虹猫蓝兔莎莉找铸剑师造剑
加载全部内容