目录
一、单例模式
1、概述
第二次梳理:一个类只能产生一个对象,饿汉式是类加载时直接创建对象,懒汉式是第一个获取该对象的时候创建,静态内部类方式是一种最佳实践,即可以实现懒加载、线程安全,还不加锁!有限多利模式是对单例模式的扩展,一个类允许产生有限数量个对象,使用者按照一定规则使用这些对象!时间:2021年05月13日 09点23分40秒
实现方式:饿汉式、懒汉式;
2、应用场景
也就是在需要频繁“创建-使用-销毁-在创建……”的情况下(经常用,但是用来用去用的东西都是一样的),使用单例模式提升性能节省资源!时间:2021年05月13日 09时43分17秒
-
需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC;
-
某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等;
-
某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用;
-
某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等;
-
频繁访问数据库或文件的对象;
-
对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套;
-
当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等;
3、优缺点
Spring的bean默认就是单例模式,可以理解为单例模式是很常用的,那些功能类(非实体类)只是为了调用方法,而没有任何属性,只需要一个单例就可以完成!时间:2021年05月13日 09时50分37秒
优点
-
只有一个实例,减少内存开销;
-
避免对资源的多重占用;
-
设置全局访问点,可以优化和共享资源的访问;
缺点
-
若要扩展,只能修改源代码,违背开闭原则;
-
唯一的实例对象,不利于代码调试;
-
功能代码写在一个类之内,容易违背单一职责原则;
4、主要角色
-
单例类:包含一个实例且能自行创建这个实例的类;
-
访问类:使用单例的类;
5、饿汉式实现
概述
类一旦接在就创建实例,保证在调用‘getInstance()
方法之前单例就已经存在了;
实体类
package com.zibo.design.one;
public class Dog {
// 声明的时候直接实例化
private static final Dog instance = new Dog();
private final String name;
private final int age;
// private,防止外部实例化
private Dog() {
this.name = "小黑";
this.age = 1;
System.out.println("Dog被实例化了!");
}
// 返回实例对象
public static Dog getInstance(){
return instance;
}
// Dog自述
public void say(){
System.out.println("我叫" + name + ",我" + age + "岁了!");
}
}
测试类
package com.zibo.design.one;
public class Test {
public static void main(String[] args) {
Dog.getInstance().say();
Dog.getInstance().say();
}
}
运行结果
Dog被实例化了!
我叫小黑,我1岁了!
我叫小黑,我1岁了!
评价
饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题;
6、懒汉式实现
概述
类加载时没有生成单例,只有当第一次调用 getlnstance()
方法时才去创建这个单例;
实体类
package com.zibo.design.one;
public class Dog {
// 使用volatile关键字,保证instance在所有线程中同步(消耗性能)
private static volatile Dog instance = null;
private final String name;
private final int age;
// private,防止外部实例化
private Dog() {
this.name = "小黑";
this.age = 1;
System.out.println("Dog被实例化了!");
}
// 使用synchronized关键字,同步方法执行(消耗性能)
public static synchronized Dog getInstance(){
if(instance == null){
instance = new Dog();
}
return instance;
}
// Dog自述
public void say(){
System.out.println("我叫" + name + ",我" + age + "岁了!");
}
}
优化后的实体类
package com.zibo.design.one;
public class Dog {
// 使用volatile关键字,保证instance在所有线程中同步(消耗性能)
private static volatile Dog instance = null;
private final String name;
private final int age;
// private,防止外部实例化
private Dog() {
this.name = "小黑";
this.age = 1;
System.out.println("Dog被实例化了!");
}
// 使用synchronized关键字,同步方法执行(消耗性能)
// 这么写能更加节省性能
// 双重验证是必要的
public static Dog getInstance(){
if(instance == null){
synchronized (Dog.class){
if(instance == null){
instance = new Dog();
}
}
}
return instance;
}
// Dog自述
public void say(){
System.out.println("我叫" + name + ",我" + age + "岁了!");
}
}
测试类与运行结果
与饿汉式一致,省略;
评价
访问时需要同步,即使优化后也会消耗额外的性能和资源;
7、静态内部类实现(推荐)
概述
能实现懒加载、是线程安全的,不用锁实现;
实体类
package com.zibo.design.one;
public class Dog {
private final String name;
private final int age;
private static class DogHelper{
private static final Dog dog = new Dog();
}
// private,防止外部实例化
private Dog() {
this.name = "小黑";
this.age = 1;
System.out.println("Dog被实例化了!");
}
// 获取实例
public static Dog getInstance(){
return DogHelper.dog;
}
// Dog自述
public void say(){
System.out.println("我叫" + name + ",我" + age + "岁了!");
}
}
测试类和运行结果
与饿汉式一致,省略;
评价
能实现懒加载,线程安全,不需要加锁,简直完美!
8、有限多例模式
概述
是对单例模式的扩展;
生成有限个实例并保存在 ArrayList 中,客户需要时可随机获取;
实体类
package com.zibo.design.one;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Dog {
// 存放多个实例的数组
private static final List<Dog> list = new ArrayList<>();
// 实例的最大数量
private static final int INSTANCE_MAX = 3;
private final String name;
private final int age;
// private,防止外部实例化
private Dog(String name, int age) {
this.name = name;
this.age = age;
}
// 使用synchronized关键字,同步方法执行(消耗性能)
// 这么写能更加节省性能
public static Dog getInstance(){
if(list.size() < INSTANCE_MAX){
synchronized (Dog.class){
if(list.size() < INSTANCE_MAX){
// 这里随机设置属性
list.add(new Dog("小黑" + new Random().nextInt(100),new Random().nextInt(10)));
}
}
}
// 随机获取一个
return list.get(new Random().nextInt(list.size()));
}
// Dog自述
public void say(){
System.out.println("我叫" + name + ",我" + age + "岁了!");
}
}
测试类
package com.zibo.design.one;
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(()-> Dog.getInstance().say()).start();
}
}
}
运行结果
我叫小黑58,我8岁了!
我叫小黑58,我8岁了!
我叫小黑85,我3岁了!
我叫小黑39,我4岁了!
我叫小黑39,我4岁了!