亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺析Spring IOC bean為什么默認(rèn)是單例

 更新時(shí)間:2023年12月14日 09:57:17   作者:Lzfnemo2009  
單例的意思就是說(shuō)在 Spring IoC 容器中只會(huì)存在一個(gè) bean 的實(shí)例,無(wú)論一次調(diào)用還是多次調(diào)用,始終指向的都是同一個(gè) bean 對(duì)象,本文小編將和大家一起分析Spring IOC bean為什么默認(rèn)是單例,需要的朋友可以參考下

首先解釋一下什么是單例 bean?

單例的意思就是說(shuō)在 Spring IoC 容器中只會(huì)存在一個(gè) bean 的實(shí)例,無(wú)論一次調(diào)用還是多次調(diào)用,始終指向的都是同一個(gè) bean 對(duì)象

用代碼來(lái)解釋單例 bean

public class UserService {
    public void sayHello() {
        System.out.println("hello");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
 
		<!-- scope 屬性就是用來(lái)設(shè)置 bean 的作用域的,不配置的話默認(rèn)就是單例,這里顯示配置了 singleton -->
    <bean id="userService" class="com.fyl.springboot.bean.singleton.UserService" scope="singleton"/>
 
</beans>
public class Demo {
 
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans-singleton.xml");
 
        UserService service = context.getBean(UserService.class);
        UserService service1 = context.getBean(UserService.class);
        System.out.println(service == service1);
    }
}

運(yùn)行 main 方法最后會(huì)輸出:true,這就很明顯的說(shuō)明了無(wú)論多少次調(diào)用 getBean 方法,最終得到的都是同一個(gè)實(shí)例。

把上面 xml 文件的配置修改一下,修改為:

<!-- scope 的值改為了 prototype,表示每次請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的 bean -->
<bean id="userService" class="com.fyl.springboot.bean.singleton.UserService" scope="prototype"/>

然后再次運(yùn)行 main 方法,結(jié)果輸出:false,說(shuō)明兩次調(diào)用 getBean 方法,得到的不是同一個(gè)實(shí)例。

了解了什么是單例 bean 之后,我們繼續(xù)來(lái)說(shuō)說(shuō)單例 bean 的線程安全問(wèn)題

為什么會(huì)存在線程安全問(wèn)題呢?

因?yàn)閷?duì)于單實(shí)例來(lái)說(shuō),所有線程都共享同一個(gè) bean 實(shí)例,自然就會(huì)發(fā)生資源的爭(zhēng)搶。

用代碼來(lái)說(shuō)明線程不安全的現(xiàn)象

public class ThreadUnSafe {
 
    public int i;
 
    public void add() {
        i++;
    }
 
    public void sub() {
        i--;
    }
 
    public int getValue() {
        return i;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
 
    <bean id="threadUnSafe" class="com.fyl.springboot.bean.singleton.ThreadUnSafe" scope="singleton"/>
 
</beans>
public class Demo {
 
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans-singleton.xml");
 
        for (int j = 0; j < 10; j++) {
            new Thread(() -> {
                ThreadUnSafe service = context.getBean(ThreadUnSafe.class);
                for (int i = 0; i < 1000; i++) {
                    service.add();
                }
                for (int i = 0; i < 1000; i++) {
                    service.sub();
                }
                System.out.println(service.getValue());
            }).start();
        }
    }
}

上面的代碼中,創(chuàng)建了 10 個(gè)線程來(lái)獲取 ThreadUnSafe 實(shí)例,并且循環(huán) 1000 次加法,循環(huán) 1000 次減法,并把最后的結(jié)果打印出來(lái)。理想的情況是每個(gè)線程打印出來(lái)的結(jié)果都是 0

先看一下運(yùn)行結(jié)果:

2073
1736
1080
1060
221
49
50
-231
-231
-231

從結(jié)果可以看出,運(yùn)行結(jié)果都不是 0,這明顯的是線程不安全啊!

為什么會(huì)出現(xiàn)這種情況?

因?yàn)?10 個(gè)線程獲取的 ThreadUnSafe 實(shí)例都是同一個(gè),并且 10 個(gè)線程都對(duì)同一個(gè)資源 i 發(fā)生了爭(zhēng)搶,所以才會(huì)導(dǎo)致線程安全問(wèn)題的發(fā)生。

現(xiàn)在把 xml 文件中的配置做一下更改:scope 的值改為 prototype

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
 
    <!-- scope 的值改為 prototype -->
    <bean id="threadUnSafe" class="com.fyl.springboot.bean.singleton.ThreadUnSafe" scope="prototype"/>
 
</beans>

然后再次運(yùn)行 main 方法,發(fā)現(xiàn)無(wú)論運(yùn)行多少次,最后的結(jié)果都是 0,是線程安全的!

因?yàn)?prototype 作用域下,每次獲取的 ThreadUnSafe 實(shí)例都不是同一個(gè),所以自然不會(huì)有線程安全的問(wèn)題。

如果單例 bean 是一個(gè)無(wú)狀態(tài)的 bean,還會(huì)有線程安全問(wèn)題嗎?

不會(huì),無(wú)狀態(tài) bean 沒(méi)有實(shí)例對(duì)象,不能保存數(shù)據(jù),是不變類,是線程安全的。

public class ThreadSafe {
 
    public void getValue() {
        int val = 0;
        for (int i = 0; i < 1000; i++) {
            val++;
        }
        for (int i = 0; i < 1000; i++) {
            val--;
        }
        System.out.println(val);
    }
}
public class Demo {
 
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans-singleton.xml");
 
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                ThreadSafe service = context.getBean(ThreadSafe.class);
                service.getValue();
            }).start();
        }
    }
}
 

運(yùn)行結(jié)果為 0

事實(shí)證明,無(wú)狀態(tài)的 bean 是線程安全的。(無(wú)狀態(tài) bean 應(yīng)該是這個(gè)意思,如有不對(duì)的地方,還望指出)

那么針對(duì)單例 bean,而且是有狀態(tài)的 bean,應(yīng)該如何保證線程安全呢?

那有人肯定會(huì)說(shuō)了:既然是線程安全問(wèn)題,那就加鎖唄!

毫無(wú)疑問(wèn)加鎖確實(shí)可以,但是加鎖多多少少有點(diǎn)性能上的下降

加鎖代碼如下所示:

public class Demo {
 
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans-singleton.xml");
 
        for (int j = 0; j < 10; j++) {
            new Thread(() -> {
                ThreadUnSafe service = context.getBean(ThreadUnSafe.class);
                synchronized (service) {
                    for (int i = 0; i < 1000; i++) {
                        service.add();
                    }
                    for (int i = 0; i < 1000; i++) {
                        service.sub();
                    }
                    System.out.println(service.getValue());
                }
            }).start();
        }
    }
}

還有一種方法是使用 ThreadLocal

ThreadLocal 簡(jiǎn)單的說(shuō)就是在自己線程內(nèi)創(chuàng)建一個(gè)變量的副本,那么線程操作的自然也就是自己線程內(nèi)的資源了,也就規(guī)避了線程安全問(wèn)題。但是卻帶來(lái)了空間上的開(kāi)銷。

使用方法如下:

public class ThreadUnSafe {
 
    ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
 
    public void add() {
        Integer i = threadLocal.get();
        if (i == null) {
            i = 0;
        }
        i++;
        threadLocal.set(i);
    }
 
    public void sub() {
        Integer i = threadLocal.get();
        i--;
        threadLocal.set(i);
    }
 
    public Integer getValue() {
        return threadLocal.get();
    }
}
public class Demo {
 
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans-singleton.xml");
 
        for (int j = 0; j < 10; j++) {
            new Thread(() -> {
                ThreadUnSafe service = context.getBean(ThreadUnSafe.class);
                for (int i = 0; i < 1000; i++) {
                    service.add();
                }
                for (int i = 0; i < 1000; i++) {
                    service.sub();
                }
                System.out.println(service.getValue());
            }).start();
        }
    }
}

使用 ThreadLocal 即使不加鎖也保證了輸出的結(jié)果都是 0

加鎖和使用 ThreadLocal 各有各的特點(diǎn)

  • 加鎖是以時(shí)間換空間
  • ThreadLocal 是以空間換時(shí)間

以上就是淺析Spring IOC bean為什么默認(rèn)是單例的詳細(xì)內(nèi)容,更多關(guān)于Spring IOC bean默認(rèn)單例的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Springboot?內(nèi)部服務(wù)調(diào)用方式

    Springboot?內(nèi)部服務(wù)調(diào)用方式

    這篇文章主要介紹了Springboot?內(nèi)部服務(wù)調(diào)用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java反射技術(shù)原理與用法實(shí)例分析

    Java反射技術(shù)原理與用法實(shí)例分析

    這篇文章主要介紹了Java反射技術(shù)原理與用法,結(jié)合實(shí)例形式分析了Java反射技術(shù)的基本概念、功能、原理、用法及操作注意事項(xiàng),需要的朋友可以參考下
    2020-04-04
  • spring boot使用自定義的線程池執(zhí)行Async任務(wù)

    spring boot使用自定義的線程池執(zhí)行Async任務(wù)

    這篇文章主要介紹了spring boot使用自定義的線程池執(zhí)行Async任務(wù)的相關(guān)資料,需要的朋友可以參考下
    2018-02-02
  • SpringCloud的Config配置中心詳解

    SpringCloud的Config配置中心詳解

    這篇文章主要介紹了SpringCloud的Config配置中心詳解,SpringCloud Config為微服務(wù)架構(gòu)中的微服務(wù)提供集中化的外部配置支持,配置服務(wù)器為各個(gè)不同微服務(wù)應(yīng)用的所有環(huán)境提供了一個(gè)中心化的外部配置,需要的朋友可以參考下
    2023-07-07
  • 教你使用Java獲取當(dāng)前時(shí)間戳的詳細(xì)代碼

    教你使用Java獲取當(dāng)前時(shí)間戳的詳細(xì)代碼

    這篇文章主要介紹了如何使用Java獲取當(dāng)前時(shí)間戳,通過(guò)兩個(gè)java示例,向大家展示如何獲取java中的當(dāng)前時(shí)間戳,文本通過(guò)示例代碼給大家展示了java獲取當(dāng)前時(shí)間戳的方法,需要的朋友可以參考下
    2022-01-01
  • SpringBoot如何配置CROS Filter

    SpringBoot如何配置CROS Filter

    這篇文章主要介紹了SpringBoot如何配置CROS Filter問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • IDEA關(guān)閉SpringBoot程序后仍然占用端口的排查與解決方法

    IDEA關(guān)閉SpringBoot程序后仍然占用端口的排查與解決方法

    在使用 IntelliJ IDEA 開(kāi)發(fā) Spring Boot 應(yīng)用時(shí),有時(shí)即使關(guān)閉了應(yīng)用,程序仍然占用端口,這會(huì)導(dǎo)致重新啟動(dòng)應(yīng)用時(shí)出現(xiàn)端口被占用的錯(cuò)誤,所以本文給大家介紹了IDEA關(guān)閉SpringBoot程序后仍然占用端口的排查與解決方法,需要的朋友可以參考下
    2025-02-02
  • jdk1.8中的for循環(huán)問(wèn)題記錄

    jdk1.8中的for循環(huán)問(wèn)題記錄

    這篇文章主要介紹了jdk1.8中的for循環(huán)及jdk1.8 新特性之 forEach 循環(huán)遍歷問(wèn)題,本文通過(guò)實(shí)例代碼給大家詳細(xì)講解,需要的朋友可以參考下
    2022-11-11
  • 深入理解JVM垃圾回收算法

    深入理解JVM垃圾回收算法

    我們都知道java語(yǔ)言與C語(yǔ)言最大的區(qū)別就是內(nèi)存自動(dòng)回收,那么JVM是怎么控制內(nèi)存回收的,這篇文章將介紹JVM垃圾回收的幾種算法,從而了解內(nèi)存回收的基本原理
    2021-06-06
  • SpringBoot鉤子函數(shù)的實(shí)現(xiàn)示例

    SpringBoot鉤子函數(shù)的實(shí)現(xiàn)示例

    SpringBoot雖然沒(méi)有直接稱為“鉤子函數(shù)”的概念,但可以其他方法實(shí)現(xiàn),本文就來(lái)介紹一下SpringBoot鉤子函數(shù)的實(shí)現(xiàn)示例,感興趣的可以了解一下
    2024-11-11

最新評(píng)論