成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring Bean 是單例的嗎?如何保證并發(fā)安全?

開(kāi)發(fā) 前端
當(dāng)然,前面提到的一些控制并發(fā)的手段(如同步機(jī)制、原子變量、ThreadLocal? 等)可以在一定程度上幫助解決本地線(xiàn)程安全問(wèn)題,但是在分布式服務(wù)環(huán)境中,確保并發(fā)安全的挑戰(zhàn)更為復(fù)雜,因?yàn)椴粌H需要處理單個(gè)應(yīng)用程序內(nèi)的多線(xiàn)程問(wèn)題,還需要應(yīng)對(duì)跨多個(gè)節(jié)點(diǎn)的并發(fā)訪(fǎng)問(wèn)。

引言

面試中,經(jīng)常會(huì)被問(wèn)到這樣一個(gè)問(wèn)題:“Spring Bean 是單例的嗎?如果是單例如何保證并發(fā)安全呢?”,這兩個(gè)問(wèn)題看似沒(méi)有關(guān)聯(lián),其實(shí)一點(diǎn)也不挨著,為什么呢?請(qǐng)聽(tīng)我來(lái)“狡辯”。

首先,單例Bean 本身并不會(huì)直接導(dǎo)致線(xiàn)程安全問(wèn)題。真正影響線(xiàn)程安全性的因素是該單例對(duì)象是否包含共享可變狀態(tài),以及在進(jìn)行并發(fā)訪(fǎng)問(wèn)時(shí)會(huì)不會(huì)因?yàn)楣蚕砜勺儬顟B(tài)而造成了數(shù)據(jù)不一致的現(xiàn)象。

其實(shí),面試官問(wèn)這樣一個(gè)問(wèn)題,大約是想考察以下幾點(diǎn)內(nèi)容:

  • 對(duì)Spring Bean 作用域的理解
  • 并發(fā)編程相關(guān)的知識(shí)(線(xiàn)程安全、同步機(jī)制等)
  • 實(shí)際項(xiàng)目中類(lèi)似問(wèn)題的處理經(jīng)驗(yàn)

好了,了解了面試官的意圖后,我們從這幾個(gè)方面來(lái)看下這個(gè)問(wèn)題應(yīng)該如何回答。

Spring 中 Bean 的作用域

Spring 官方定義的Bean Scopes 有如下幾種,出自Spring 5.1.6.RELEASE 版本文檔

圖片圖片

Bean scopes

  • singleton:Spring 中默認(rèn)的作用域。被定義為singleton 的Bean 實(shí)例,在第一次被請(qǐng)求獲取時(shí)創(chuàng)建出來(lái),并緩存起來(lái)供后續(xù)使用。也就是說(shuō),在整個(gè)Spring 容器中,該Bean 只有一個(gè)實(shí)例存在。無(wú)論你多少次請(qǐng)求這個(gè)Bean,Spring 都會(huì)返回同一個(gè)對(duì)象實(shí)例。
  • prototype:表示每次獲取該Bean 時(shí),Spring 容器都會(huì)創(chuàng)建一個(gè)新的實(shí)例。這種作用域適用于那些不應(yīng)該被共享的對(duì)象,例如有狀態(tài)的Bean。
  • request:在Web 應(yīng)用程序中,request 作用域的Bean 在一次HTTP 請(qǐng)求期間有效。對(duì)于每個(gè)新的HTTP 請(qǐng)求,Spring 會(huì)創(chuàng)建該Bean 的一個(gè)新實(shí)例。一旦請(qǐng)求完成,Bean 就會(huì)被銷(xiāo)毀。每個(gè)請(qǐng)求都有其獨(dú)立的Bean 實(shí)例,這非常適合處理與特定請(qǐng)求相關(guān)的狀態(tài)信息,如表單數(shù)據(jù)或用戶(hù)認(rèn)證信息。
  • session:類(lèi)似于request 作用域,但session 作用域的Bean 在一個(gè)HTTP Session 期間有效。也就是說(shuō),在同一個(gè)用戶(hù)Session 內(nèi),所有對(duì)這個(gè)Bean 的請(qǐng)求都將共享同一個(gè)實(shí)例;而當(dāng)Session 結(jié)束時(shí),Bean 也會(huì)被銷(xiāo)毀。適用于存儲(chǔ)用戶(hù)的會(huì)話(huà)等相關(guān)信息。
  • application:這個(gè)作用域的Bean 在ServletContext(即整個(gè)Web 應(yīng)用程序)的生命周期內(nèi)有效。這意味著在整個(gè)應(yīng)用程序運(yùn)行期間,只會(huì)存在一個(gè)這樣的Bean 實(shí)例,類(lèi)似于singleton,但它是在ServletContext 中唯一的,而不是在整個(gè)Spring 容器中唯一的。
  • websocket:這個(gè)作用域的Bean 在WebSocket 連接期間有效。意思是每個(gè)WebSocket 連接都有自己的Bean 實(shí)例,這些實(shí)例僅在該連接存活期間可用。當(dāng)WebSocket 連接關(guān)閉時(shí),Bean 實(shí)例將被銷(xiāo)毀。

Spring 中也支持自定義 Scope(這里不做贅述):

  • 實(shí)現(xiàn) org.springframework.beans.factory.config.Scope 接口
  • 調(diào)用 org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope 方法注冊(cè)到容器中

單例 Bean 如何保證并發(fā)安全?

線(xiàn)程安全問(wèn)題引發(fā)因素

  • 多線(xiàn)程:多線(xiàn)程的運(yùn)行環(huán)境,同一個(gè)程序中,多個(gè)線(xiàn)程并發(fā)執(zhí)行。
  • 產(chǎn)生競(jìng)態(tài)條件:當(dāng)一個(gè)對(duì)象內(nèi)部包含可變狀態(tài)時(shí),就可能產(chǎn)生競(jìng)態(tài)條件(Race Condition),即不同線(xiàn)程之間的操作順序會(huì)影響最終結(jié)果。

示例:假設(shè)有一個(gè)單例Bean 處理訂單業(yè)務(wù)邏輯,并且它維護(hù)了一個(gè)內(nèi)部計(jì)數(shù)器來(lái)跟蹤訂單數(shù)量。如果不采取任何同步措施,多個(gè)線(xiàn)程同時(shí)調(diào)用該placeOrder 方法可能會(huì)導(dǎo)致計(jì)數(shù)器值錯(cuò)誤。

@Component
public class OrderService {
    private int orderCount = 0;
    public void placeOrder() {
        orderCount++; // 可能發(fā)生競(jìng)態(tài)條件
        System.out.println("Placed order, total orders: " + orderCount);
    }
}

解決方案

  • 無(wú)狀態(tài)設(shè)計(jì):盡量設(shè)計(jì)成無(wú)狀態(tài)的Bean,即Bean 不持有任何可變狀態(tài)。這樣即使多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)也不會(huì)有問(wèn)題。對(duì)于確實(shí)需要維護(hù)狀態(tài)的情況,可以通過(guò)參數(shù)傳遞或外部化狀態(tài)來(lái)實(shí)現(xiàn)。
@Component
public class StatelessOrderService {
    // 不再維護(hù)訂單狀態(tài)
    public void placeOrder(Order order) {
        // 訂單處理邏輯
        System.out.println("Placed order: " + order.getId());
    }
}

什么是無(wú)狀態(tài)與有狀態(tài)對(duì)象?

無(wú)狀態(tài)的對(duì)象 (Stateless Object):無(wú)狀態(tài)對(duì)象是指那些不保持任何內(nèi)部狀態(tài)的對(duì)象。它們的行為完全由方法參數(shù)決定,這意味著每次調(diào)用相同的方法并傳入相同的參數(shù),都將得到一致的結(jié)果,而不受之前操作的影響。

有狀態(tài)的對(duì)象 (Stateful Object):有狀態(tài)對(duì)象維護(hù)內(nèi)部狀態(tài),并且這些狀態(tài)可能會(huì)影響對(duì)象的行為。這種狀態(tài)通常是通過(guò)成員變量存儲(chǔ)的,在對(duì)象的生命期內(nèi)可以發(fā)生變化。

  • 不可變對(duì)象:如果一個(gè)對(duì)象一旦創(chuàng)建后就不會(huì)改變,那么它自然是線(xiàn)程安全的。通過(guò)使用final 關(guān)鍵字確保字段不可修改,并避免對(duì)外暴露可變狀態(tài)。
public final class ImmutableOrder {
    private final String id;
    private final String customerName;

    public ImmutableOrder(String id, String customerName) {
        this.id = id;
        this.customerName = customerName;
    }

    public String getId() {
        return id;
    }

    public String getCustomerName() {
        return customerName;
    }
}
  • 同步機(jī)制:對(duì)于那些需要維護(hù)內(nèi)部狀態(tài)的Bean,可以通過(guò)synchronized 關(guān)鍵字來(lái)同步方法或代碼塊,從而確保同一時(shí)刻只有一個(gè)線(xiàn)程能夠訪(fǎng)問(wèn)這些方法或代碼塊。還可以使用更細(xì)粒度的鎖機(jī)制,如ReentrantLock。
@Component
public class SynchronizedOrderService {
    private int orderCount = 0;

    public synchronized void placeOrder() {
        orderCount++;
        System.out.println("Placed order, total orders: " + orderCount);
    }
}
  • 線(xiàn)程安全的數(shù)據(jù)結(jié)構(gòu):使用JUC(java.util.concurrent) 提供的線(xiàn)程安全集合類(lèi)(如ConcurrentHashMap、CopyOnWriteArrayList)和原子變量(如AtomicInteger、AtomicLong)等,可以在不加鎖的情況下完成對(duì)數(shù)值的操作,提高性能。
import java.util.concurrent.ConcurrentHashMap;

@Component
public class ThreadSafeOrderService {
    private final Map<String, Integer> orderCounts = new ConcurrentHashMap<>();

    public void placeOrder(String customerId) {
        orderCounts.compute(customerId, (id, count) -> count == null ? 1 : count + 1);
        System.out.println("Placed order for customer " + customerId + ", total orders: " + orderCounts.get(customerId));
    }
}
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class AtomicOrderService {
    private final AtomicInteger orderCount = new AtomicInteger(0);

    public void placeOrder() {
        int currentCount = orderCount.incrementAndGet();
        System.out.println("Placed order, total orders: " + currentCount);
    }
}
  • ThreadLocal 變量:利用ThreadLocal 提供的每個(gè)線(xiàn)程私有的變量副本,可以避免多個(gè)線(xiàn)程之間互相干擾。如果需要在線(xiàn)程間傳遞上下文時(shí)可以使用這種方式。
import java.util.HashMap;
import java.util.Map;

@Component
public class ThreadLocalOrderService {
    private static final ThreadLocal<Map<String, Integer>> threadLocalOrders = ThreadLocal.withInitial(HashMap::new);

    public void placeOrder(String customerId) {
        Map<String, Integer> orders = threadLocalOrders.get();
        orders.merge(customerId, 1, Integer::sum);
        System.out.println("Placed order for customer " + customerId + ", total orders in this thread: " + orders.get(customerId));
    }
}

結(jié)語(yǔ)

當(dāng)然,前面提到的一些控制并發(fā)的手段(如同步機(jī)制、原子變量、ThreadLocal 等)可以在一定程度上幫助解決本地線(xiàn)程安全問(wèn)題,但是在分布式服務(wù)環(huán)境中,確保并發(fā)安全的挑戰(zhàn)更為復(fù)雜,因?yàn)椴粌H需要處理單個(gè)應(yīng)用程序內(nèi)的多線(xiàn)程問(wèn)題,還需要應(yīng)對(duì)跨多個(gè)節(jié)點(diǎn)的并發(fā)訪(fǎng)問(wèn)。彼時(shí),我們可以借助Redis、Zookeeper 等分布式中間件來(lái)控制多個(gè)服務(wù)節(jié)點(diǎn)的并發(fā)。

責(zé)任編輯:武曉燕 來(lái)源: Java驛站
相關(guān)推薦

2022-09-16 08:42:23

JavaAPI變量

2024-11-26 07:29:57

高并發(fā)線(xiàn)程安全

2024-11-26 17:43:51

2022-11-22 08:01:30

2021-04-29 07:18:21

Spring IOC容器單例

2016-09-19 10:01:08

NodeJSWeb

2023-10-16 11:12:29

2021-06-08 11:15:10

Redis數(shù)據(jù)庫(kù)命令

2023-05-15 08:01:16

Go語(yǔ)言

2024-05-20 13:13:01

線(xiàn)程安全Java

2016-10-10 23:00:18

2021-07-07 12:36:10

HTTPSSSL通信

2021-03-15 07:02:02

java線(xiàn)程安全

2021-07-01 10:45:18

Bean對(duì)象作用域

2021-05-11 07:42:59

BeanSpring屬性

2023-10-08 10:14:12

2024-01-11 15:17:59

Bean單例模式線(xiàn)程安全

2021-01-29 08:19:50

HTTPS安全傳輸

2011-09-23 10:13:43

2024-06-17 00:02:00

線(xiàn)程安全HashMapJDK 1.7
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产一区二区免费 | 一区二区视频在线观看 | 国产麻豆乱码精品一区二区三区 | 亚洲视频一区在线观看 | 国产在线97 | 99re在线视频| 欧美男人天堂 | 日韩精品无码一区二区三区 | 中文字幕不卡在线观看 | 桃花av在线 | 91看片| 国产精品成人一区二区三区 | 91一区二区 | 特级黄一级播放 | 久久久久国色av免费观看性色 | 国产高清在线精品一区二区三区 | www.av在线| 一区二区三区四区视频 | 精品一区免费 | 亚洲精品成人网 | 久久婷婷麻豆国产91天堂 | www.欧美视频 | 一区二区三区免费网站 | 天堂一区 | 神马久久久久久久久久 | 丁香六月激情 | 国产成人精品免高潮在线观看 | 色播av| 一区二区精品 | 国产免费观看一区 | 亚洲高清在线免费观看 | 欧美在线一区二区三区 | 伦理片97 | 免费在线视频一区二区 | 免费在线观看一区二区三区 | 亚洲一区二区在线 | 中文在线一区 | 毛片a级 | 午夜欧美 | 久久综合狠狠综合久久 | h在线免费观看 |