<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          為什么局部變量是線程安全的?

          共 2701字,需瀏覽 6分鐘

           ·

          2020-10-28 20:53


          前言


          方法中的變量(即局部變量)是不存在數(shù)據(jù)競爭(Data Race)的,也是線程安全的。為了理解為什么,我們先來了一下方法是如何被執(zhí)行的,然后再分析局部變量的安全性,最后再介紹利用局部變量不會共享的特點而產(chǎn)生的解決并發(fā)問題的一些技術(shù)。


          方法是如何被執(zhí)行的


          int?a = 7
          int[] b = fibonacci(a);
          int[] c = b;


          以上代碼轉(zhuǎn)換成CPU指令執(zhí)行,方法的調(diào)用過程示意圖如下:(圖來自參考[1])



          當(dāng)調(diào)用fibonacci(a)時,CPU要先找到方法fibonacci()的地址(在CPU堆棧寄存器中),然后跳轉(zhuǎn)到這個地址去執(zhí)行代碼(藍(lán)色線),最后CPU執(zhí)行完方法,再返回原來調(diào)用方法的下一條語句(紅色線)。


          CPU找調(diào)用方法的參數(shù)和返回地址,是通過堆棧寄存器。CPU支持一種線性結(jié)構(gòu),因為與方法調(diào)用有關(guān),所以也稱為調(diào)用棧


          再舉個例子,有三個方法A、B、C。方法A中調(diào)用方法B,方法B中調(diào)用方法C。那么將會構(gòu)建出如下調(diào)用棧。每個方法在調(diào)用棧里都有自己的獨立空間,稱為棧幀。每個棧幀都有對應(yīng)方法需要的參數(shù)和返回地址。當(dāng)調(diào)用新方法時,會創(chuàng)建新的棧幀,并壓入調(diào)用棧;當(dāng)方法返回時,對應(yīng)的棧幀就會被自動彈出。即,棧幀和方法同生共死。



          三個方法生成的調(diào)用棧如上圖所示。


          不同的編程語言雖定義方法雖各有所異,但是它們執(zhí)行方法的原理卻是一致的:都是依靠棧結(jié)構(gòu)解決。Java語言雖然是靠虛擬機(jī)解釋執(zhí)行,但是方法的調(diào)用也是利用棧結(jié)構(gòu)解決的。


          局部變量的存放位置


          局部變量是定義在方法內(nèi),作用域也是在方法內(nèi)部。當(dāng)方法運行結(jié)束后,局部變量也就失效了。那么我們可以得出,局部變量的存放位置應(yīng)該在調(diào)用棧中。事實上,局部變量就是存放到調(diào)用棧中的



          調(diào)用棧與線程


          兩個線程可以同時用不同的參數(shù)調(diào)用相同的方法,那么調(diào)用棧和線程之間是什么關(guān)系呢?答案就是:每個線程都有自己獨立的調(diào)用棧



          所以,Java方法里面的局部變量是不存在并發(fā)問題的。每個線程都有自己獨立的調(diào)用棧,局部變量保存在各自的調(diào)用棧中,不會被共享,自然也就沒有并發(fā)問題。


          利用不共享解決并發(fā)問題的技術(shù): 線程封閉


          當(dāng)多線程訪問沒有同步的可變共享變量時就會出現(xiàn)并發(fā)問題,而解決方案之一便是使變量不共享。變量不會和其他變量共享,也就不會存在并發(fā)問題。僅在單線程里訪問數(shù)據(jù),不需要同步,我們稱之為線程封閉。當(dāng)某個對象封閉在一個線程中時,這種用法將自動實現(xiàn)線程安全性,即使被封閉的對象本身不是線程安全的。


          采用線程封閉技術(shù)的案例非常多。例如一種常見的應(yīng)用便為JDBC的Connection對象。從數(shù)據(jù)庫連接池中獲取一個Connection對象,在JDBC規(guī)范中并沒有要求這個Connection一定是線程安全的。數(shù)據(jù)庫連接池通過線程封閉技術(shù),保證一個Connection對象一旦被一個線程獲取之后,在這個Connection對象返回之前,連接池不會將它分配給其他線程,從而保證了Connection對象不會有并發(fā)問題。


          線程封閉技術(shù)的一個具體實現(xiàn)是我們上面提到的局部變量的使用(棧封閉),還有一種需要提一下,即ThreadLocal類。


          ThreadLoacl類


          維持線程封閉性一種更規(guī)范方法是使用ThreadLocal,這個類能使線程中的某個值與保存值的對象相關(guān)聯(lián)起來。ThreadLocal提供了get()和set()等訪問接口,這些方法為每個使用該變量的線程都存有一份獨立的副本,因此get()總是返回由當(dāng)前執(zhí)行線程在調(diào)用set()時設(shè)置的最新值。


          ThreadLocal對象通常用于防止對可變的單實例變量(Singleton)或全局變量進(jìn)行共享


          例如,在單線程應(yīng)用程序中可能會維持一個全局的數(shù)據(jù)庫連接,并在線程啟動時初始化這個連接對象,從而避免在調(diào)用每個方法時都要傳遞一個Connection對象。由于JDBC的連接對象不一定線程安全的,因此,當(dāng)多線程應(yīng)用程序在沒有協(xié)同的情況下使用全局變量時,就不是線程安全的。通過將JDBC的連接保存到ThreadLocal對象中,每個線程都會擁有屬于自己的連接。


          如以下代碼所示,利用ThreadLocal來維持線程的封閉性:(代碼來自參考[2])


          public?class?ConnectionDispenser?{
          ????static?String DB_URL = "jdbc:mysql://localhost/mydatabase";

          ????private?ThreadLocal connectionHolder
          ????????= new?ThreadLocal() {
          ????????public?Connection initialValue() {
          ????????????try?{
          ????????????????return?DriverManager.getConnection(DB_URL);
          ????????????} catch?(SQLException e) {
          ????????????????throw?new?RuntimeException("Unable to acquire Connection, e");
          ????????????}
          ????????};
          ????};

          ????public?Connection getConnection() {
          ????????return?connectionHolder.get();
          ????}
          }


          當(dāng)某個頻繁執(zhí)行的操作需要一個臨時對象,例如一個緩沖區(qū),而同時又希望避免在每次執(zhí)行時都重新分配該臨時對象,就可以使用這項技術(shù)。例如,在Java 5.0之前,Integer.toString()方法使用ThreadLocal對象來保存一個12字節(jié)大小的緩沖區(qū),用于對結(jié)果進(jìn)行格式化,而不是使用共享的靜態(tài)緩沖區(qū)(需要使用加鎖機(jī)制)或者每次調(diào)用時都分配一個新的緩沖區(qū)。


          小結(jié)


          知道方法是如何調(diào)用的也就明白了局部變量為什么是線程安全的。方法調(diào)用會產(chǎn)生棧幀,局部變量會放在棧幀的工作內(nèi)存中,線程之間不共享,故不存在線程安全問題。后面我們介紹了基于不共享解決并發(fā)問題的線程封閉技術(shù),除了不共享這種思想可以解決并發(fā)問題,還有兩種:使用不可變變量和正確使用同步機(jī)制。


          原文鏈接:cnblogs.com/myworld7/p/12264504.html



          瀏覽 65
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  小电影在线观看黄.999 | 狼人综合影院 | 日韩中文字幕AV | 午夜视频黄色 | 婷婷亚洲五月天 |