面試官:Spring MVC 如何保證 Controller 的併發安全性?面試必問。。

来源:https://www.cnblogs.com/javastack/archive/2022/05/08/16244851.html
-Advertisement-
Play Games

來源:https://www.toutiao.com/article/6927297421139706376 單例模式(Singleton)是程式設計中一種非常重要的設計模式,設計模式也是Java面試重點考察的一個方面。面試經常會問到的一個問題是:SpringMVC中的Controller是單例還是 ...


來源:https://www.toutiao.com/article/6927297421139706376

單例模式(Singleton)是程式設計中一種非常重要的設計模式,設計模式也是Java面試重點考察的一個方面。面試經常會問到的一個問題是:SpringMVC中的Controller是單例還是多例,很多同學可能會想當然認為Controller是多例,其實不然。

根據Tomcat官網中的介紹,對於一個瀏覽器請求,tomcat會指定一個處理線程,或是線上程池中選取空閑的,或者新建一個線程。

Each incoming request requires a thread for the duration of that request. If more simultaneous requests are received than can be handled by the currently available request processing threads, additional threads will be created up to the configured maximum (the value of the maxThreads attribute). If still more simultaneous requests are received, they are stacked up inside the server socket created by the Connector, up to the configured maximum (the value of the acceptCountattribute). Any further simultaneous requests will receive "connection refused" errors, until resources are available to process them.

—— https://tomcat.apache.org/tomcat-7.0-doc/config/http.html

在Tomcat容器中,每個servlet是單例的。在SpringMVC中,Controller 預設也是單例。 採用單例模式的最大好處,就是可以在高併發場景下極大地節省記憶體資源,提高服務抗壓能力。

單例模式容易出現的問題是:在Controller中定義的實例變數,在多個請求併發時會出現競爭訪問,Controller中的實例變數不是線程安全的。

Spring Boot 基礎就不介紹了,推薦下這個實戰教程:https://github.com/javastacks/spring-boot-best-practice

Controller不是線程安全的

正因為Controller預設是單例,所以不是線程安全的。如果用SpringMVC 的 Controller時,儘量不在 Controller中使用實例變數,否則會出現線程不安全性的情況,導致數據邏輯混亂。

舉一個簡單的例子,在一個Controller中定義一個非靜態成員變數 num 。通過Controller成員方法來對 num 增加。

@Controller
public class TestController {
    private int num = 0;

    @RequestMapping("/addNum")
    public void addNum() {
        System.out.println(++num);
    }
}

在本地運行後:

  • 首先訪問 http:// localhost:8080 / addNum,得到的答案是1;
  • 再次訪問 http:// localhost:8080 / addNum,得到的答案是 2。
  • 兩次訪問得到的結果不同,num已經被修改,並不是我們希望的結果,介面的冪等性被破壞。

從這個例子可以看出,所有的請求訪問同一個Controller實例,Controller的私有成員變數就是線程共用的。某個請求對應的線程如果修改了這個變數,那麼在別的請求中也可以讀到這個變數修改後的的值。

Controller併發安全的解決辦法

如果要保證Controller的線程安全,有以下解決辦法:

  • 儘量不要在 Controller 中定義成員變數
  • 如果必須要定義一個非靜態成員變數,那麼可以通過註解 @Scope(“prototype”) ,將Controller設置為多例模式。
@Controller
@Scope(value="prototype")
public class TestController {
    private int num = 0;

    @RequestMapping("/addNum")
    public void addNum() {
        System.out.println(++num);
    }
}

Scope屬性是用來聲明IOC容器中的對象(Bean )允許存在的限定場景,或者說是對象的存活空間。在對象進入相應的使用場景之前,IOC容器會生成並裝配這些對象;當該對象不再處於這些使用場景的限定時,容器通常會銷毀這些對象。

Controller也是一個Bean,預設的 Scope 屬性為Singleton ,也就是單例模式。如果Bean的 Scope 屬性設置為 prototype 的話,容器在接受到該類型對象的請求時,每次都會重新生成一個新的對象給請求方。

  • Controller 中使用 ThreadLocal 變數。 每一個線程都有一個變數的副本。
public class TestController {
    private int num = 0;
    private final ThreadLocal <Integer> uniqueNum =
             new ThreadLocal <Integer> () {
                 @Override protected Integer initialValue() {
                     return num;
                 }
             };

    @RequestMapping("/addNum")
    public void addNum() {
        int unum = uniqueNum.get();
       uniqueNum.set(++unum);
       System.out.println(uniqueNum.get());
    }
}

以上代碼運行以後,每次請求 http:// localhost:8080 / addNum , 得到的結果都是1。

更嚴格的做法是用AtomicInteger類型定義成員變數,對於成員變數的操作使用AtomicInteger的自增方法完成。

總的來說,還是儘量不要在 Controller 中定義成員變數為好。

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了。。。

3.Spring Boot 2.x 教程,太全了!

4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!

5.《Java開發手冊(嵩山版)》最新發佈,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 介紹瞭如何在程式代碼中嵌入IPython用於調試,並分析了優點與不足 ...
  • C++進階-3-6-map/multimap容器 1 #include<iostream> 2 #include<map> 3 using namespace std; 4 5 // map / multimap容器 6 7 void printMap(map<int, int>& m) { 8 f ...
  • 模塊與包 一、Python 模塊簡介 在開發過程中,隨著程式代碼越寫越多,在一個文件里代碼就會越來越長,越來越不容易維護。 後面我們學習了函數,知道函數是實現一項或多項功能的一段程式,這樣就更方便我們重覆使用代碼。 緊接著,我們有學了類,類可以封裝方法和變數(屬性)。這樣就更方便我們維護代碼了。 我 ...
  • 爬取圖片實例 •selenium+win32爬取圖片 Python學習交流Q群:903971231##### """爬取圖片""" import os import threading import time from ctypes import windll import requests imp ...
  • C++進階-3-5-set/multiset容器 1 #include<iostream> 2 #include<set> 3 using namespace std; 4 5 // set/multiset容器 6 7 void printSet(set<int>& s) { 8 9 for (s ...
  • 還記得Java 16中的instanceof增強 嗎? 通過下麵這個例子再回憶一下: Map<String, Object> data = new HashMap<>(); data.put("key1", "aaa"); data.put("key2", 111); if (data.get("k ...
  • Matplotlib 教程 Matplotlib 是 Python 的繪圖庫,它能讓使用者很輕鬆地將數據圖形化,並且提供多樣化的輸出格式。 Matplotlib 可以用來繪製各種靜態,動態,互動式的圖表。 Matplotlib 是一個非常強大的 Python 畫圖工具,我們可以使用該工具將很多數據通 ...
  • 一、背景介紹 最近幾天,谷愛凌在冬奧會賽場上奪得一枚寶貴的金牌,為中國隊貢獻了自己的榮譽! 針對此熱門事件,我用Python的爬蟲和情感分析技術,針對小破站的彈幕數據,分析了眾網友彈幕的輿論導向,下麵我們來看一下,是如何實現的分析過程。 二、代碼講解-爬蟲部分 2.1 分析彈幕介面 首先分析B站彈幕 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文將以 C# 語言來實現一個簡單的布隆過濾器,為簡化說明,設計得很簡單,僅供學習使用。 感謝@時總百忙之中的指導。 布隆過濾器簡介 布隆過濾器(Bloom filter)是一種特殊的 Hash Table,能夠以較小的存儲空間較快地判斷出數據是否存在。常用於允許一定誤判率的數據過濾及防止緩存 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 「簡單有價值的事情長期堅持做」 這是成功最簡單,但也最難學的秘訣。不經過訓練,人很難意識到時間複利的威力。 仙劍奇俠傳的「十里坡劍神」和金庸群俠傳的「十級野球拳」,就是簡單的事情持之以恆反覆做,最後就有巨大的威力 唐家三少成為網文收入第一,最重要的一步是十四年從未斷日更 這樣的案例很多,一開始可能成 ...
  • 迎面走來了你的面試官,身穿格子衫,挺著啤酒肚,髮際線嚴重後移的中年男子。 手拿泡著枸杞的保溫杯,胳膊夾著MacBook,MacBook上還貼著公司標語:“我愛加班”。 面試開始,直入正題。 面試官: 看你簡歷上面寫著精通MySQL,我先問你事務的特性是什麼? 老生常談,這個還有誰不會背的嗎? 我: ...
  • 基礎知識 python是一門腳本語言,它是解釋執行的。 python使用縮進做為語法,而且python2環境下同一個py文件中不能同時存在tab和空格縮進,否則會出錯,建議在IDE中顯示縮進符。 python在聲明變數時不寫數據類型,可以type(xx)來獲取欄位的類型,然後可以int(),list ...
  • 為什麼要多線程下載 俗話說要以終為始,那麼我們首先要明確多線程下載的目標是什麼,不外乎是為了更快的下載文件。那麼問題來了,多線程下載文件相比於單線程是不是更快? 對於這個問題可以看下圖。 橫坐標是線程數,縱坐標是使用對應線程數下載對應文件時花費的時間,藍橙綠代表下載文件的大小,每個線程下載對應文件2 ...
  • 詳細講解python爬蟲代碼,爬微博搜索結果的博文數據。 爬取欄位: 頁碼、微博id、微博bid、微博作者、發佈時間、微博內容、轉發數、評論數、點贊數。 爬蟲技術: 1、requests 發送請求 2、datetime 時間格式轉換 3、jsonpath 快速解析json數據 4、re 正則表達式提... ...
  • 背景: 一般我們可以用HashMap做本地緩存,但是HashMap功能比較弱,不支持Key過期,不支持數據範圍查找等。故在此實現了一個簡易的本地緩存,取名叫fastmap。 功能: 1.支持數據過期 2.支持等值查找 3.支持範圍查找 4.支持key排序 實現思路: 1.等值查找採用HashMap2 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 本章是系列文章的第八章,用著色演算法進行寄存器的分配過程。 本文中的所有內容來自學習DCC888的學習筆記或者自己理解的整理,如需轉載請註明出處。周榮華@燧原科技 寄存器分配 寄存器分配是為程式處理的值找到存儲位置的問題 這些值可以存放到寄存器,也可以存放在記憶體中 寄存器更快,但數量有限 記憶體很多,但 ...