空間站

          北極心空

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks
          多線程
          線程:是指進程中的一個執(zhí)行流程。
          線程與進程的區(qū)別:每個進程都需要操作系統(tǒng)為其分配獨立的內存地址空間,而同一進程中的所有線程在同一塊地址空間中工作,這些線程可以共享同一塊內存和系統(tǒng)資源。


          如何創(chuàng)建一個線程?

          創(chuàng)建線程有兩種方式,如下:
          1、 擴展java.lang.Thread類
          2、 實現Runnable接口
          Thread類代表線程類,它的兩個最主要的方法是:
          run()——包含線程運行時所執(zhí)行的代碼
          Start()——用于啟動線程

          一個線程只能被啟動一次。第二次啟動時將會拋出java.lang.IllegalThreadExcetpion異常

          線程間狀態(tài)的轉換(如圖示)

          新建狀態(tài):用new語句創(chuàng)建的線程對象處于新建狀態(tài),此時它和其它的java對象一樣,僅僅在堆中被分配了內存
          就緒狀態(tài):當一個線程創(chuàng)建了以后,其他的線程調用了它的start()方法,該線程就進入了就緒狀態(tài)。處于這個狀態(tài)的線程位于可運行池中,等待獲得CPU的使用權
          運行狀態(tài):處于這個狀態(tài)的線程占用CPU,執(zhí)行程序的代碼
          阻塞狀態(tài):當線程處于阻塞狀態(tài)時,java虛擬機不會給線程分配CPU,直到線程重新進入就緒狀態(tài),它才有機會轉到運行狀態(tài)。
          阻塞狀態(tài)分為三種情況:
          1、 位于對象等待池中的阻塞狀態(tài):當線程運行時,如果執(zhí)行了某個對象的wait()方法,java虛擬機就回把線程放到這個對象的等待池中
          2、 位于對象鎖中的阻塞狀態(tài),當線程處于運行狀態(tài)時,試圖獲得某個對象的同步鎖時,如果該對象的同步鎖已經被其他的線程占用,JVM就會把這個線程放到這個對象的瑣池中。
          3、 其它的阻塞狀態(tài):當前線程執(zhí)行了sleep()方法,或者調用了其它線程的join()方法,或者發(fā)出了I/O請求時,就會進入這個狀態(tài)中。

          死亡狀態(tài):當線程退出了run()方法,就進入了死亡狀態(tài),該線程結束了生命周期。
          或者正常退出
          或者遇到異常退出
          Thread類的isAlive()方法判斷一個線程是否活著,當線程處于死亡狀態(tài)或者新建狀態(tài)時,該方法返回false,在其余的狀態(tài)下,該方法返回true.

          線程調度
          線程調度模型:分時調度模型和搶占式調度模型
          JVM采用搶占式調度模型。
          所謂的多線程的并發(fā)運行,其實是指宏觀上看,各個線程輪流獲得CPU的使用權,分別執(zhí)行各自的任務。
          (線程的調度不是跨平臺,它不僅取決于java虛擬機,它還依賴于操作系統(tǒng))

          如果希望明確地讓一個線程給另外一個線程運行的機會,可以采取以下的辦法之一
          1、 調整各個線程的優(yōu)先級
          2、 讓處于運行狀態(tài)的線程調用Thread.sleep()方法
          3、 讓處于運行狀態(tài)的線程調用Thread.yield()方法
          4、 讓處于運行狀態(tài)的線程調用另一個線程的join()方法

          調整各個線程的優(yōu)先級
          Thread類的setPriority(int)和getPriority()方法分別用來設置優(yōu)先級和讀取優(yōu)先級。
          如果希望程序能夠移值到各個操作系統(tǒng)中,應該確保在設置線程的優(yōu)先級時,只使用MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY這3個優(yōu)先級。

          線程睡眠:當線程在運行中執(zhí)行了sleep()方法時,它就會放棄CPU,轉到阻塞狀態(tài)。
          線程讓步:當線程在運行中執(zhí)行了Thread類的yield()靜態(tài)方法時,如果此時具有相同優(yōu)先級的其它線程處于就緒狀態(tài),那么yield()方法將把當前運行的線程放到運行池中并使另一個線程運行。如果沒有相同優(yōu)先級的可運行線程,則yield()方法什么也不做。
          Sleep()方法和yield()方法都是Thread類的靜態(tài)方法,都會使當前處于運行狀態(tài)的線程放棄CPU,把運行機會讓給別的線程,兩者的區(qū)別在于:
          1、sleep()方法會給其他線程運行的機會,而不考慮其他線程的優(yōu)先級,因此會給較低線程一個運行的機會;yield()方法只會給相同優(yōu)先級或者更高優(yōu)先級的線程一個運行的機會。
          2、當線程執(zhí)行了sleep(long millis)方法后,將轉到阻塞狀態(tài),參數millis指定睡眠時間;當線程執(zhí)行了yield()方法后,將轉到就緒狀態(tài)。
          3、sleep()方法聲明拋出InterruptedException異常,而yield()方法沒有聲明拋出任何異常
          4、sleep()方法比yield()方法具有更好的移植性

          等待其它線程的結束:join()
          當前運行的線程可以調用另一個線程的 join()方法,當前運行的線程將轉到阻塞狀態(tài),直到另一個線程運行結束,它才恢復運行。

          定時器Timer:在JDK的java.util包中提供了一個實用類Timer, 它能夠定時執(zhí)行特定的任務。

          線程的同步
          原子操作:根據Java規(guī)范,對于基本類型的賦值或者返回值操作,是原子操作。但這里的基本數據類型不包括long和double, 因為JVM看到的基本存儲單位是32位,而long 和double都要用64位來表示。所以無法在一個時鐘周期內完成。

          自增操作(++)不是原子操作,因為它涉及到一次讀和一次寫。

          原子操作:由一組相關的操作完成,這些操作可能會操縱與其它的線程共享的資源,為了保證得到正確的運算結果,一個線程在執(zhí)行原子操作其間,應該采取其他的措施使得其他的線程不能操縱共享資源。

          同步代碼塊:為了保證每個線程能夠正常執(zhí)行原子操作,Java引入了同步機制,具體的做法是在代表原子操作的程序代碼前加上synchronized標記,這樣的代碼被稱為同步代碼塊。

          同步鎖:每個JAVA對象都有且只有一個同步鎖,在任何時刻,最多只允許一個線程擁有這把鎖。

          當一個線程試圖訪問帶有synchronized(this)標記的代碼塊時,必須獲得 this關鍵字引用的對象的鎖,在以下的兩種情況下,本線程有著不同的命運。
          1、 假如這個鎖已經被其它的線程占用,JVM就會把這個線程放到本對象的鎖池中。本線程進入阻塞狀態(tài)。鎖池中可能有很多的線程,等到其他的線程釋放了鎖,JVM就會從鎖池中隨機取出一個線程,使這個線程擁有鎖,并且轉到就緒狀態(tài)。
          2、 假如這個鎖沒有被其他線程占用,本線程會獲得這把鎖,開始執(zhí)行同步代碼塊。
          (一般情況下在執(zhí)行同步代碼塊時不會釋放同步鎖,但也有特殊情況會釋放對象鎖
          如在執(zhí)行同步代碼塊時,遇到異常而導致線程終止,鎖會被釋放;在執(zhí)行代碼塊時,執(zhí)行了鎖所屬對象的wait()方法,這個線程會釋放對象鎖,進入對象的等待池中)

          線程同步的特征:
          1、 如果一個同步代碼塊和非同步代碼塊同時操作共享資源,仍然會造成對共享資源的競爭。因為當一個線程執(zhí)行一個對象的同步代碼塊時,其他的線程仍然可以執(zhí)行對象的非同步代碼塊。(所謂的線程之間保持同步,是指不同的線程在執(zhí)行同一個對象的同步代碼塊時,因為要獲得對象的同步鎖而互相牽制)
          2、 每個對象都有唯一的同步鎖
          3、 在靜態(tài)方法前面可以使用synchronized修飾符。
          4、 當一個線程開始執(zhí)行同步代碼塊時,并不意味著必須以不間斷的方式運行,進入同步代碼塊的線程可以執(zhí)行Thread.sleep()或者執(zhí)行Thread.yield()方法,此時它并不釋放對象鎖,只是把運行的機會讓給其他的線程。
          5、 Synchronized聲明不會被繼承,如果一個用synchronized修飾的方法被子類覆蓋,那么子類中這個方法不在保持同步,除非用synchronized修飾。

          線程安全的類:
          1、 這個類的對象可以同時被多個線程安全的訪問。
          2、 每個線程都能正常的執(zhí)行原子操作,得到正確的結果。
          3、 在每個線程的原子操作都完成后,對象處于邏輯上合理的狀態(tài)。

          釋放對象的鎖:
          1、 執(zhí)行完同步代碼塊就會釋放對象的鎖
          2、 在執(zhí)行同步代碼塊的過程中,遇到異常而導致線程終止,鎖也會被釋放
          3、 在執(zhí)行同步代碼塊的過程中,執(zhí)行了鎖所屬對象的wait()方法,這個線程會釋放對象鎖,進入對象的等待池。

          死鎖
          當一個線程等待由另一個線程持有的鎖,而后者正在等待已被第一個線程持有的鎖時,就會發(fā)生死鎖。JVM不監(jiān)測也不試圖避免這種情況,因此保證不發(fā)生死鎖就成了程序員的責任。

          如何避免死鎖
          一個通用的經驗法則是:當幾個線程都要訪問共享資源A、B、C 時,保證每個線程都按照同樣的順序去訪問他們。

          線程通信
          Java.lang.Object類中提供了兩個用于線程通信的方法
          1、 wait():執(zhí)行了該方法的線程釋放對象的鎖,JVM會把該線程放到對象的等待池中。該線程等待其它線程喚醒
          2、 notify():執(zhí)行該方法的線程喚醒在對象的等待池中等待的一個線程,JVM從對象的等待池中隨機選擇一個線程,把它轉到對象的鎖池中。
          • 2803608e-b6f9-3ac8-8784-fad314b98cda-thumb
          • 描述:
          • 大小: 701.5 KB
          用synchronized修飾的方法被子類繼承并重寫,當子類運行其重寫后的synchronized方法時,相當于調用了2次用synchronized修飾的方法,這是怎么回事?


          (1)對于instance函數,關鍵字synchronized其實并不鎖定函數和代碼,它鎖定的是對象。請記住每個對象只有一個lock與之關聯。
          (2)Synchronized聲明不會被繼承,如果一個用synchronized修飾的方法被子類覆蓋,那么子類中這個方法不在保持同步,除非用synchronized修飾。
          (3)子類中重寫了synchronized修飾的方法后,當調用這個子類方法時,很顯然線程競爭的就是這個子類對象的lock,只有獲得這個lock的線程才會執(zhí)行該方法體。
          (4)不明白你說的當子類運行其重寫后的synchronized方法時,相當于調用了2次用synchronized修飾的方法,怎么會呢?你是怎么調的?我很好奇!能不能把你的代碼貼出來看下?呵呵!
          posted on 2008-06-25 10:00 蘆葦 閱讀(250) 評論(0)  編輯  收藏 所屬分類: JAVA
          主站蜘蛛池模板: 江西省| 阿勒泰市| 夏河县| 富阳市| 新余市| 前郭尔| 磴口县| 晋宁县| 合水县| 吴江市| 重庆市| 林西县| 宁武县| 开化县| 九江县| 东乌珠穆沁旗| 秦皇岛市| 丹阳市| 祁门县| 开化县| 石狮市| 建阳市| 通许县| 木兰县| 仁布县| 沈丘县| 阳泉市| 和平县| 秦皇岛市| 揭阳市| 靖西县| 太仆寺旗| 师宗县| 杭锦后旗| 电白县| 宜兰县| 呼玛县| 朝阳市| 乐清市| 滨海县| 宾阳县|