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;}}}




