单例模式
一个单一的类,负责创建自己的对象,同时确保系统中只有单个对象被创建。
单例特点
- 某个类只能有一个实例(构造器私有)
- 它必须自行创建这个实例(自己编写实例化逻辑)
- 它必须自行向整个系统提供这个实例(对外提供实例化方法)
懒汉式
package com.xx.design.creatation.singleton;
public class Person {
private String name;
private String age;
// 懒汉:没有再去创建
private static Person instance;
// 饿汉式
// private final static Person instance = new Person();
// 构造器私有,外部不能实例化
private Person(){
System.out.println("创建了Person对象");
}
// 提供给外部的方法
public static Person getInstance(){
// 如果没有实例再去创建
if(instance == null){
Person person = new Person();
instance = person;
}
return instance;
}
}
package com.xx.design.creatation.singleton;
// 测试函数
public class MainTest {
public static void main(String[] args) {
Person person1 = Person.getInstance();
Person person2 = Person.getInstance();
System.out.println(person1 == person2);
/*
创建了Person对象
true
*/
}
}
多线程存在问题
public static Person getInstance(){
// 如果多线程的情况下,多个人一起进入这个方法,同时进入if
// 同时判断是null , 那么大家会同时去创建对象,所以存在问题
if(instance == null){
Person person = new Person();
instance = person;
}
return instance;
}
// 方法一:加锁(锁方法)
// getInstance方法也是一个static方法,所以是加在类上面,创建对象用的是同一个类,所以会锁住
public static synchronized Person getInstance(){
if(instance == null){
Person person = new Person();
instance = person;
}
return instance;
}
// 方法二:加锁(锁代码块),区别不大
public static Person getInstance(){
synchronized (Person.class){
if(instance == null){
Person person = new Person();
instance = person;
}
}
return instance;
}
双重检查
public static Person getInstance() {
// 如果对象为空,再加锁,再来创建对象
// 可以发现同样存在多个线程一起进来,都判断对象为空,创建多个对象
if (instance == null) {
synchronized (Person.class) {
Person person = new Person();
instance = person;
}
}
return instance;
}
//再加一次判断
public static Person getInstance() {
if (instance == null) {
synchronized (Person.class) {
// 前面一个人进来,创建对象,第二个人就可以不用创建对象
if(instance == null){
// 但是依然存在问题
// 因为赋值操作,会被封装成先创建对象,然后分配地址等等
// 所以需要用 volatile 关键字修饰
Person person = new Person();
instance = person;
}
}
}
return instance;
}
应用场景
- 多线程中的线程池
- 数据库的连接池
- 系统环境信息
- 上下文(ServletContext)
- …
面试问题
- 系统环境信息(System.getProperties())?
- Spring中怎么保持组件单例的
- SerletContext是什么,是单例吗,怎么保证
- ApplicationContext是什么,是单例吗,怎么保证
-
- tomcat:一个应用(部署的一个war包)会有一个应用上下文
-
- spring:表示整个IOC容器(单例),ioc容器中有很多组件(单例)
- 数据库连接池怎么创建出来,保证单例的
- …