1. 单例(singleton):只有一个共享的实例存在,所有对这个bean的请求都会返回这个唯一的实例。eg:每个国家(spring)有一个总统(bean),国家的所有人(其他调用者)共享此总统。
2. 多例(pototype):对这个bean每次请求都会创建一个新的bean实例,类似于new
3. SpringBoot采用的是单例模式、@Component注解默认实例化的对象是单例,如果想声明成多例对象,可以使用@Scope(“pototype”)、@Respository默认单例、@Service默认单例、@Controller默认单例
4. 单例bean与单例模式:
(1)单例模式:在一个JVM进程中仅有一个实例(一个运行的Java程序必定有一个自己独立的JVM),无论在程序的何处获取实例,始终都返回一个对象 eg:Java内置的Runtime,无论在程序的何处获取实例,Runtime.getRuntime == Runtime.getRuntime 结果为true(例如建立目录、数据库的连接都需要单线程操作),单例模式能避免实例的重复创建、避免多个实例引起程序逻辑错误、节约内存。
(2)Spring单例bean:Spring的单例bean是与其容器(ApplicationContext)相关的,所以在一个JVM进程中,若有多个Spring容器,即使是单例bean,也会创建多个实例(如果在实际应用中,对象的状态会改变,就使用多例)
5. Spring把bean默认设置为单例的原因:
(1)提高性能,减少了新生成实例的消耗
(2)减少了JVM的垃圾回收
(3)可以快速获取到bean(单例的获取除了第一次生成之外都是从缓存中获取的)
6. 七种创建单例的方式:
(1)饿汉式:
public class Singleton1{
private static Singleton1 singleton1 = new Singleton1();
public static Singleton1 getSingleton1(){
return singleton1;
}
}
复制
singleton1作为类变量直接得到初始化,优点是在多线程环境下能够保证同步,不可能被实例化两次,但是如果singleton1在很长一段时间后才使用,意味着singleton1实例开辟的堆内存会驻留很长时间,不能延迟加载,占用内存
(2)懒汉式:
public class Singleton2{
private static Singleton2 singleton1 = null;
public static Singleton2 getSingleton1(){
if(singleton1 ==null){
singleton1 = new Singleton2();
}
return singleton1;
}
}
复制
懒汉式,是在使用的时候才去创建,这样可以避免类在初始化时提前创建,但是如果在多线程的情况下,若一开始因为线程上下文切换的原因,导致两个线程都通过了if判断,这样就new出两个实例,无法保证唯一性。延迟加载,但在多线程环境下不安全。
(3)懒汉式+同步:
public class Singleton3 {
private static Singleton3 singleton1 = null;
public synchronized Singleton3 getSingleton1() {
if (singleton1 == null) {
singleton1 = new Singleton3();
}
return singleton1;
}
}
复制
这种方法通过添加同步控制,满足了懒加载和singleton1实例的唯一性,但是添加同步控制后,getSingleton1()方法是串行化的,获取时需要排队等候,效率较低。
(4)懒汉式+双重检验:
ublic class Singleton4 {
private static Singleton4 singleton1 = null;
public static Singleton4 getSingleton1() {
if (singleton1 == null) {
synchronized (Singleton4.class) {
if (singleton1 == null) {
singleton1 = new Singleton4();
}
}
}
return singleton1;
}
}
复制
若有两个线程通过了第一个check,进入第二个check是串行化的,只能有一个线程进入,保证了唯一性,也不会有同步控制,可能会引起空指针异常,但是可能会出现JVM排序的问题
(假设这个单例创建有一些其他的资源,例如Socket、Connection,这些资源在构造函数中也会被实例化,那样在创建单例的时候,就是要实例化自身还有Socket这些资源,那根据JVM的重排序和Happens-before原则,有可能会出现先实例化自身,再去实例化Socket这些资源,若在此时只实例化了自己的情况下,别的线程调用了这个单例中Socket这些资源的方法,而此时它们可能还没有被实例化,这样就会抛出空指针的异常)
(5)懒汉式+双重检验+volatile
Volatile关键字禁止指令重排序:
public class Singleton5 {
private static volatile Singleton5 singleton1 = null;
public static Singleton5 getSingleton1() {
if (singleton1 == null) {
synchronized (Singleton5.class) {
if (singleton1 == null) {
singleton1 = new Singleton5();
}
}
}
return singleton1;
}
}
复制
(6)Holder方式(静态内部类):
public class Singleton6 {
private Singleton6(){
}
private static class Singleton{
private static Singleton6 singleton1 = new Singleton6();
}
public static Singleton6 getInstance(){
return Singleton.singleton1;
}
}
复制
在Singleton6中并没有singleton1的静态成员,而是放在静态内部类Singleton中,因此Singleton6的初始化并不会实例化singleton1,只有当Singleton被主动引用的时候才会实例化。
(7)枚举法:
public class Singleton7 {
private Singleton7(){
}
private enum Singleton{
Singleton;
private final Singleton7 singleton7;
Singleton(){
singleton7 = new Singleton7();
}
public Singleton7 getSingleton7() {
return singleton7;
}
}
}
复制