<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      JVM學(xué)習(xí)記錄-線程安全與鎖優(yōu)化(一)

      前言

      線程:程序流執(zhí)行的最小單元。線程是比進(jìn)程更輕量級(jí)的調(diào)度執(zhí)行單位,線程的引入,可以把一個(gè)進(jìn)程的資源分配和執(zhí)行調(diào)度分開,各個(gè)線程既可以共享進(jìn)程資源(內(nèi)存地址、文件I/O等),又可以獨(dú)立調(diào)度(線程是CPU調(diào)度的基本單位)。

      Java語(yǔ)言定義了5中線程狀態(tài),在任意一個(gè)時(shí)間點(diǎn),一個(gè)線程只能有且只有其中的一種狀態(tài),5中狀態(tài)如下。

      新建(New):創(chuàng)建后尚未啟動(dòng)的線程處于這種狀態(tài)。

      運(yùn)行(Runnable):Runnable包括了操作系統(tǒng)線程狀態(tài)中的Running和Ready,也就是處于此狀態(tài)的線程可能正在執(zhí)行,也可能正在等待著CPU為它分配執(zhí)行時(shí)間。

      無(wú)限期等待(Waiting):處于這種狀態(tài)的線程不會(huì)被分配CPU執(zhí)行時(shí)間,它們要等待被其他線程顯示地喚醒。

      讓線程進(jìn)入無(wú)限等待的方法有如下幾個(gè):

      • 沒有設(shè)置Timeout參數(shù)的Object.wait()方法。
      • 沒有設(shè)置Timeout參數(shù)的Thread.join()方法。
      • LockSupport.park()方法。

      限期等待(Timed Waiting):處于這種狀態(tài)的線程也不會(huì)被分配CPU執(zhí)行時(shí)間,不過(guò)無(wú)須等待被其他線程顯式地喚醒,在一定時(shí)間之后它們會(huì)由系統(tǒng)自動(dòng)喚醒。

      讓線程進(jìn)入限期等待狀態(tài)的方法有如下幾個(gè):

      • Thread.sleep()方法。
      • 設(shè)置了Timeout參數(shù)的Object.wait()方法。
      • 設(shè)置了Timeout參數(shù)的Thread.join()方法。
      • LockSupport.parkNanos()方法。
      • LockSupport.parkUntil()方法。

      阻塞(Blocked):線程被阻塞了,“阻塞狀態(tài)”是在等待著獲取到一個(gè)排他鎖,這個(gè)事件將在另一個(gè)線程放棄這個(gè)鎖的時(shí)候發(fā)生;更通俗的解釋就是一個(gè)線程正在干著一件事,沒資源干其他的事,當(dāng)來(lái)了其他的事時(shí)就只能阻塞的等著線程能騰出時(shí)間來(lái)處理。

      結(jié)束(Terminated):已終止線程的線程狀態(tài),線程已經(jīng)結(jié)束執(zhí)行。

      這5種狀態(tài)在遇到特定的事件的時(shí)候會(huì)相互轉(zhuǎn)換。

      線程安全

      一個(gè)比較嚴(yán)謹(jǐn)線程安全定義:當(dāng)多個(gè)線程訪問一個(gè)對(duì)象時(shí),如果不用考慮這些線程在運(yùn)行時(shí)環(huán)境下的調(diào)度和交替執(zhí)行,也不需要進(jìn)行額外的同步,或者在調(diào)用方進(jìn)行任何其他的協(xié)調(diào)操作,調(diào)用這個(gè)對(duì)象的行為都可以獲得正確地結(jié)果,那么這個(gè)對(duì)象就是線程安全的。

      Java語(yǔ)言中的線程安全

      研究線程安全,需要限定于多個(gè)線程之間存在共享數(shù)據(jù)訪問這個(gè)前提。Java語(yǔ)言中各種操作共享數(shù)據(jù)分為以下5類:

      不可變

      在JDK1.5以后,Java語(yǔ)言中不可變的對(duì)象一定是線程安全的,無(wú)論是對(duì)象的方法實(shí)現(xiàn)還是方法的調(diào)用者,都不需要再采取任何的線程安全保障措施。如果一個(gè)基本數(shù)據(jù)類在定義時(shí)使用final關(guān)鍵字修飾它,就可以保證它時(shí)不可變的。如果final修飾的是一個(gè)對(duì)象,需要保證對(duì)象的方法不會(huì)對(duì)其狀態(tài)產(chǎn)生影響才行。例如:String類的substring()、concat()這些方法不會(huì)影響原來(lái)的值,只會(huì)生成一個(gè)新的字符串。

      保證對(duì)象方法不會(huì)對(duì)其狀態(tài)產(chǎn)生影響的實(shí)現(xiàn)方式有很多,最簡(jiǎn)單是將對(duì)象中帶有狀態(tài)的屬性用final修飾。

      例如Integer類中的實(shí)現(xiàn)代碼:

         /**
           * The value of the {@code Integer}.
           *
           * @serial
           */
          private final int value;
      
          /**
           * Constructs a newly allocated {@code Integer} object that
           * represents the specified {@code int} value.
           *
           * @param   value   the value to be represented by the
           *                  {@code Integer} object.
           */
          public Integer(int value) {
              this.value = value;
          }

      Java中除了String類、Integer類,還有其他的Long、Double等包裝類,以及BigInteger和BigDecimal等大數(shù)據(jù)類型,都符合不可變要求的類型。

      絕對(duì)線程安全

      絕對(duì)線程安全,是指絕對(duì)的符合前面提到的線程安全的定義,多線程永遠(yuǎn)調(diào)用對(duì)象時(shí)永遠(yuǎn)都能獲得正確的結(jié)果。但是為了實(shí)現(xiàn)這個(gè)絕對(duì)要付出的代價(jià)是很大的,在Java中標(biāo)注自己是線程安全的類,絕大多數(shù)都不是絕對(duì)線程安全的,例如Vector類,java.util.Vector是一個(gè)線程安全類,它的add()、get()、size()等都是被synchronized修飾的,但這并不能保證它是絕對(duì)安全的。

      如下代碼:

      public class Test {
          
          private static Vector<Integer> vector = new Vector<Integer>();
      
          public static void main(String[] args){
              
      
              while (true){
                  for(int index = 0;index < 10;index++){
                      vector.add(index);
                  }
                  //移除元素的線程
                  Thread removeThread = new Thread(new Runnable() {
                      @Override
                      public void run() {
                          for (int i = 0; i<vector.size(); i++){
                              vector.remove(i);
                          }
                      }
                  });
      
                  //打印元素的線程
                  Thread printThread = new Thread(new Runnable() {
                      @Override
                      public void run() {
                          for (int i = 0; i<vector.size(); i++){
                              System.out.println(vector.get(i));
                          }
                      }
                  });
      
                  removeThread.start();
                  printThread.start();
             //別創(chuàng)建太多線程,出現(xiàn)異常就手動(dòng)停止運(yùn)行吧,不然會(huì)一直執(zhí)行下去。
                  while (Thread.activeCount()>5);
              }
      
      
          }
          
      
      }

      運(yùn)行結(jié)果:

      Exception in thread "Thread-229" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 15
          at java.util.Vector.get(Vector.java:748)
          at com.eurekaclient2.client2.shejimoshi.JVM.Test$2.run(Test.java:38)
          at java.lang.Thread.run(Thread.java:748)

      盡管Vector的方法都是同步的,但是在多線程環(huán)境下,若不在調(diào)用方法端做額外的同步措施的話,仍然不是線程安全的,因?yàn)槿袅硪痪€程恰好在錯(cuò)誤的時(shí)間里刪除了一個(gè)元素,導(dǎo)致序號(hào)i已經(jīng)不再可用的話,再用i訪問數(shù)組就會(huì)拋出一個(gè)ArrayIndexOutOfBoundsException。

      解決方法如下(將移除和打印都設(shè)置為同步):

      public class Test {
      
          private static Vector<Integer> vector = new Vector<Integer>();
      
          public static void main(String[] args){
      
      
              while (true){
                  for(int index = 0;index < 10;index++){
                      vector.add(index);
                  }
                  //移除元素的線程
                  Thread removeThread = new Thread(new Runnable() {
                      @Override
                      public void run() {
                          synchronized(vector){
                              for (int i = 0; i<vector.size(); i++){
                                  vector.remove(i);
                              }
                          }
                      }
                  });
      
                  //打印元素的線程
                  Thread printThread = new Thread(new Runnable() {
                      @Override
                      public void run() {
                          synchronized(vector){
                              for (int i = 0; i<vector.size(); i++){
                                  System.out.println(vector.get(i));
                              }
                          }
                      }
                  });
      
                  removeThread.start();
                  printThread.start();
      
                  while (Thread.activeCount()>5);
              }
      
      
          }
      
      
      }

      相對(duì)線程安全

      我們通常所講的線程安全就是指的相對(duì)線程安全,需要保證對(duì)象單獨(dú)的操作時(shí)線程安全的,不需要做額外的保障措施。但若是對(duì)于一些特定屬性的連續(xù)調(diào)用,就可能會(huì)需要在調(diào)用端添加額外的同步措施。Java語(yǔ)言中,大部分的線程安全類都是相對(duì)線程安全的,例如Vector、HashTable以及Collections的synchronizedCollection()方法包裝的集合等。

      線程兼容

      線程兼容是指對(duì)象本身不是線程安全的,但是可以通過(guò)在調(diào)用端使用同步措施來(lái)保證對(duì)象在并發(fā)環(huán)境中可以安全的使用。Java中大部分類都是線程兼容的,如ArrayList、HashMap等等。

      線程對(duì)立

      線程對(duì)立指無(wú)論調(diào)用端是否采用了同步措施,都無(wú)法在多線程環(huán)境中并發(fā)使用代碼。這種代碼是有害的,應(yīng)盡量避免。常見的線程對(duì)立操作有System.setIn()、System.setOut()和System.runFinalizersOnExit()等。

      線程安全的實(shí)現(xiàn)方法

       線程安全的實(shí)現(xiàn)主要有以下幾個(gè)方法:

      互斥同步

      通過(guò)互斥來(lái)實(shí)現(xiàn)同步,臨界區(qū)、互斥量、信號(hào)量都是主要的互斥實(shí)現(xiàn)方法。在Java中最基本的互斥同步手段就是synchronized關(guān)鍵字,synchronized關(guān)鍵字通過(guò)編譯后,會(huì)在同步塊的前后分別形成monitorenter和monitorexit這兩個(gè)字節(jié)碼指令,這兩個(gè)字節(jié)碼都需要一個(gè)reference類型的慘呼是來(lái)指明要鎖定和解鎖的對(duì)象。若在程序中為synchronized指明了對(duì)象參數(shù),那就是這個(gè)對(duì)象的reference,若沒有指明,則根據(jù)synchronized修飾的是實(shí)例方法或類方法,來(lái)獲取對(duì)應(yīng)的對(duì)象或Class對(duì)象來(lái)作為鎖對(duì)象。

      在虛擬機(jī)規(guī)范中要求,在執(zhí)行monitorernter指令時(shí),首先要嘗試獲取對(duì)象的鎖。如若此對(duì)象沒被鎖定或當(dāng)期線程已經(jīng)擁有了此對(duì)象的鎖,則把鎖的計(jì)數(shù)器加1,響應(yīng)的在執(zhí)行monitorexit指令時(shí)會(huì)將鎖計(jì)數(shù)器減1,當(dāng)計(jì)數(shù)器為0時(shí),鎖就會(huì)被釋放。若獲取鎖失敗,那么當(dāng)前線程就要進(jìn)入阻塞狀態(tài),直到對(duì)象鎖被另外一個(gè)線程釋放為止。

      有兩點(diǎn)需要注意的是:

      • synchronized同步快對(duì)于同一條線程來(lái)說(shuō)是可重入的,不會(huì)出現(xiàn)自己把自己鎖死的問題。
      • 同步塊在已進(jìn)入的線程執(zhí)行完之前,會(huì)阻塞后面其他線程的進(jìn)入。

      除了synchronized之外,還可以使用java.util.concurrent包中的重入鎖(ReentrantLock)來(lái)實(shí)現(xiàn)同步。用法很相似,只是代碼寫法上有區(qū)別,ReentrantLock表現(xiàn)為API層面的互斥鎖(lock()和unlock()方法配合try/finally()語(yǔ)句塊來(lái)完成),synchronized表現(xiàn)為原生語(yǔ)法層面的互斥鎖。不過(guò)ReentrantLock比synchronized增加了一些高級(jí)功能,主要有以下3項(xiàng):等待可中斷、可實(shí)現(xiàn)公平鎖,以及鎖可以綁定多個(gè)條件。

      • 等待可中斷是指當(dāng)持有鎖的線程長(zhǎng)期不釋放鎖的時(shí)候,正在等待的線程可以選擇放棄等待,改為處理其他事情,可中斷特性對(duì)處理執(zhí)行時(shí)間非常長(zhǎng)的同步塊很有幫助。
      • 公平鎖是指多個(gè)線程在等待同一個(gè)鎖時(shí),必須按照申請(qǐng)鎖的時(shí)間順序來(lái)依次獲得鎖;非公平鎖,在釋放時(shí)任何一個(gè)等待線程都有機(jī)會(huì)獲得鎖。synchronized是非公平鎖,ReentrantLock默認(rèn)情況下也是非公平的,但可以通過(guò)帶布爾值的構(gòu)造函數(shù)要求使用公平鎖。
      • 鎖綁定多個(gè)條件是指一個(gè)ReentrantLock對(duì)象可以同時(shí)綁定多個(gè)Condition對(duì)象,而synchronized中,鎖對(duì)象的wait()和notify()或notifyAll()方法可以實(shí)現(xiàn)一個(gè)隱含的條件,如果要和多于一個(gè)的條件關(guān)聯(lián)的時(shí)候,就不得不額外的添加一個(gè)鎖,而ReentrantLock則無(wú)須這樣做,只需要多次調(diào)用newCondition()方法即可。

      非阻塞同步

      互斥同步最主要的問題就是進(jìn)行現(xiàn)場(chǎng)阻塞和喚醒鎖帶來(lái)的性能問題,因此這種同步也稱為阻塞同步(Block Synchronization)。從處理問題的方式上來(lái)說(shuō),互斥同步屬于一種悲觀的并發(fā)策略,那么相對(duì)而言的就有了另一種基于沖突檢測(cè)的樂觀并發(fā)策略,通俗的解釋就是先執(zhí)行操作,如果沒有其他線程爭(zhēng)用共享數(shù)據(jù),那操作就成功了;如果有線程爭(zhēng)用共享數(shù)據(jù),那就再采取其他補(bǔ)償措施(常見的補(bǔ)償措施就是不斷重試,直到成功為止),這種樂觀的并發(fā)策略不需要把線程掛起,因此也被稱為非阻塞同步(Non-Block Synchronization)

      在進(jìn)行操作和沖突檢測(cè)時(shí),需要保證這兩個(gè)步驟的原子性,這個(gè)時(shí)候如果靠同步互斥,那就也成悲觀并發(fā)了,所以只能靠硬件來(lái)完成這個(gè)保證,硬件保證一個(gè)從語(yǔ)義上開起來(lái)需要多次操作的行為只通過(guò)一條處理器指令就能完成,此類指令常用的有:

      • 測(cè)試并設(shè)置(Test-and-Set)。
      • 獲取并增加(Fetch-and-Increment)。
      • 交換(Swap)。
      • 比較并交換(Compare-and-Swap)CAS
      • 加載鏈接/條件存儲(chǔ)(Load-Linked/Store-COnditional)。

      無(wú)同步方案

      可重入代碼

      如果一個(gè)方法,它的返回結(jié)果是可以預(yù)測(cè)的,只要輸入了相同的數(shù)據(jù),就都能返回相同的結(jié)果,那它就滿足可重入性的要求,當(dāng)然也就是線程安全的。這個(gè)方法就是可重入代碼,在這段代碼可以在執(zhí)行的任何時(shí)刻中斷它,轉(zhuǎn)而去執(zhí)行另外一段代碼,而在控制權(quán)返回后,原來(lái)的程序不會(huì)出現(xiàn)任何錯(cuò)誤。

      線程本地存儲(chǔ)

      如果一段代碼中所需要的數(shù)據(jù)必須與其他代碼共享,那就看看這些共享數(shù)據(jù)的代碼是否能保證在同一個(gè)線程中執(zhí)行?如果能保證,我們就可以把共享數(shù)據(jù)的可見范圍限制在同一個(gè)線程之內(nèi),這樣,無(wú)須同步也能保證線程之間不出現(xiàn)數(shù)據(jù)爭(zhēng)用的問題。例如大部分的消息隊(duì)列的架構(gòu)模式(生產(chǎn)者-消費(fèi)者)都符合這個(gè)特點(diǎn)。

       

       

       

       

       

       

       

      posted @ 2018-06-02 19:50  紀(jì)莫  閱讀(791)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 少妇粗大进出白浆嘿嘿视频| 午夜通通国产精品福利| 么公的好大好硬好深好爽视频| 亚洲日韩AV秘 无码一区二区| 亚洲精品久久无码av片软件| 国产在线观看码高清视频| 亚洲欧洲日韩精品在线| 国产香蕉久久精品综合网| 国产无遮挡猛进猛出免费软件| 国内精品久久久久影院薰衣草| 国产精品视频一品二区三| 国产精品免费看久久久| 精品一区二区三区免费视频| 男女动态无遮挡动态图| 福州市| 高中女无套中出17p| 人妻人人做人碰人人添| 亚洲香蕉网久久综合影视| 丰满高跟丝袜老熟女久久| 欧美xxxxhd高清| 乱人伦人妻中文字幕无码久久网| 国产激情一区二区三区不卡| 午夜综合网| 国内免费视频成人精品| 国产精品一码二码三码| 日本丰满少妇裸体自慰| 九九热在线视频精品免费| 福利视频在线一区二区| 女高中生自慰污污网站| 成年女性特黄午夜视频免费看| 免费AV片在线观看网址| 普宁市| 曰韩无码二三区中文字幕| 在线亚洲妇色中文色综合| 美女禁区a级全片免费观看| 野花香视频在线观看免费高清版| 亚洲熟妇自偷自拍另类| 久久精品国产亚洲av麻豆长发| 免费人成在线观看网站| A级毛片无码久久精品免费| 免费的特黄特色大片|