1. 程式人生 > >瘋狂java講義:多執行緒(一)

瘋狂java講義:多執行緒(一)

第十六章  多執行緒(一)

    並行:在同一時刻,有多條指令在多個處理器上同時執行

    併發:同一時刻只有一個指令被處理器執行,但多個程序指令快速輪換執行,使得巨集觀好像多個指令在同時執行

    作業系統可以同時執行多個任務,每個任務就是程序;程序可以同時執行多個任務,每個任務就是執行緒

執行緒的建立和啟動

    1.繼承Thread類建立執行緒

        步驟:1.定義Thread子類,重寫run(),run()即為執行緒執行體

                 2.建立Thread子類例項,即建立執行緒物件

                 3.呼叫執行緒物件的start()方法啟動執行緒

        執行緒之間無法共享執行緒類的例項變數

    2.實現Runnable藉口建立執行緒類

        步驟:1.定義Runnable介面,重寫run()方法

                 2.建立Runnable例項,以此例項作為Thread的target來建立Thread物件,該Thread才是真正的執行緒物件

                可用Lambda表示式建立例項

                 3.呼叫執行緒物件的start()方法來啟動執行緒

              eg: new Thread(RunnableThread, RunnableName).start()

              多執行緒共享實現執行緒類的例項變數

    3.使用Callable和Future建立執行緒

        步驟:1.建立Callable實現類,並實現call()方法

                        call()是隻執行體,且有返回值

                        可使用Lambda表示式建立Callable物件

                  2.使用FutureTask類包裝Callable物件,該FutureTask物件封裝了該Callable物件的call()方法的返回值

                  3.使用FutureTask物件作為Thread物件的target建立並啟動新執行緒

                  4.使用FutureTask物件的get()方法來獲得子執行緒執行結束後返回值

              eg:

                     FutureTask<Integer> task = new FutureTask<Integer>(CallableThread);

                     new Thread(task, taskName).start()

              call()方法可以有返回值

              call()方法可以丟擲異常

            Future介面定義如下方法;

                boolbean cancel(boolbean mayInterrupIfRunning)    取消Future關聯的callable任務

                get()    獲取執行緒返回值,或阻塞。直到拿到返回值

                isCancelled()    如果任務正常完成前被取消,返回true

                isDone()    任務完成返回true

    4.建立執行緒的三種方式的對比

        使用Runnable、Callable介面的優缺點:

            優點:1.執行緒類只是實現Runnable介面和callable介面,還可以繼承其他類

                      2.多個執行緒可以共享一個target物件,非常適合多個相同執行緒處理同一份資源的情況下。從而可以將CPU、程式碼、和資料分開。

            劣勢:程式設計複雜,如果需要訪問當前執行緒,則必須使用Thread.currentThread()方法

        使用Thread類建立執行緒優缺點:

            優點:編寫簡單,如果需要訪問當前執行緒直接使用This

            缺點:已經繼承Thread,無法再繼承其他父類

執行緒的生命週期:

    新建new - 就緒Runnable - 執行Running - 阻塞Blocked - 死亡Dead

    1.new一個執行緒之後,是新建狀態

    2.start()執行後,就緒狀態

    3.如果就緒狀態的執行緒獲得CPU,開始執行run(),則是出於執行狀態

    4.如下情況會進入阻塞狀態:

        1.執行緒呼叫sleep()

        2.呼叫了阻塞式IO

        3.執行緒試圖獲得一個同步監視器,但是該同步監視器正在被其他執行緒佔用

        4.執行緒在等待某個通知notify

        5.程式呼叫了執行緒的suspend()方法將該執行緒掛起  / 可通過resume()恢復

        被阻塞的執行緒阻塞結束後,會再次進入就緒狀態,再次等待CPU,然後進入執行狀態

        yield()方法可使執行狀態轉為就緒狀態

    5.執行緒會以一下三種方式結束,結束後處於死亡狀態

        1.run()、call()方法執行完畢,執行緒正常結束

        2.執行緒丟擲未捕獲異常和錯誤

        3.直接呼叫stop()方法結束執行緒

        可通過isAlive()方法,判斷執行緒是否死亡

        Note:程式只能對新建狀態的執行緒執行start()方法。只能執行一次。

控制執行緒:

    join執行緒:當程式呼叫其他執行緒的join()時,呼叫執行緒將會被阻塞,直到join()方法加入的join執行緒執行完畢,呼叫執行緒才會繼續執行。

    後臺執行緒:在後臺執行,為其他執行緒提供服務。如果所有執行緒都死亡,後臺執行緒會自動死亡。

          Thread物件的setDaemon(true)方法可將制定執行緒設定為後臺執行緒

          isDaemon()方法用於判斷執行緒是否為後臺執行緒。

          前臺執行緒建立的執行緒預設為前臺執行緒,後臺執行緒建立的執行緒預設為後臺執行緒。

          要將某個執行緒設定為後臺執行緒,必須在該執行緒啟動前設定,setDaemon(true)必須在start()方法前呼叫

    執行緒睡眠:sleep(),讓目前正在執行的執行緒暫停一段時間,執行緒進入阻塞狀態,時間到了之後,進入就緒狀態

    sleep()和yield()的區別:1.sleep()方法執行後,執行緒阻塞,然後讓其他執行緒執行;yield()方法只會給優先順序相同,或者優先順序更高的執行緒執行機會。

                2.sleep(),讓目前正在執行的執行緒暫停一段時間,執行緒進入阻塞狀態,時間到了之後,進入就緒狀態;yield()方法不會將執行緒進入阻塞狀態,直接進入就緒狀態。

                3.sleep()方法宣告丟擲InterruptedException異常,所以sleep需要捕捉該異常,或者顯示宣告丟擲異常;而yield()沒有宣告丟擲任何異常

                4.sleep()方法比yield()方法更具有移植性

    設定執行緒優先順序:

        每個執行緒優先順序預設與父執行緒優先順序相同。

        main執行緒具有普遍優先順序,main建立的子執行緒具有普遍優先順序。

        setPriority()設定並返回執行緒優先順序,引數整型,1~10; MAX_PRIORITY 10;MIN_PRIORITY 1; NORM_PRIORITY 5

執行緒同步:

    同步程式碼塊:

        synchronized(obj)

        {

          同步程式碼塊

        }

        程式執行行,必須先獲得對同步監視器的鎖定;任何時刻只有一個執行緒可以獲得對同步監視器的鎖定。

        在加鎖期間其他執行緒無法修改該資源。

    同步方法:

        public/private/protect synchronized void mothodname()

        {

          方法體

        }

        synchronized不可以修飾static方法

        無需顯示指定同步監視器,同步方法的同步監視器是this,也就是呼叫該方法的物件。

    synchronized可以修飾程式碼塊和方法但是不可以修飾構造器,成員變數。

    同步鎖:顯示定義同步鎖物件實現同步。

        1.定義鎖物件:private final ReentrantLook lock =  new ReentrantLock();

        2.加鎖:lock.lock()

        3.執行方法體

        4.釋放鎖:lock.unlock()

        Lock控制多個執行緒對共享資源進行訪問。通常所提供獨佔訪問,每次只能有一個執行緒對Lock物件加鎖,執行緒開始訪問共享資源前需要先獲得Lock物件。

ThreadLocal執行緒區域性變數

    ThreadLocal:執行緒區域性變數,為每一個使用該變數的執行緒都提供一個變數值的副本,使得每一個執行緒都可以獨立的改變自己的副本,而不會與其他執行緒的副本產生衝突。

    T get(); 返回此區域性變數在該執行緒中的副本值

    void remove():刪除此區域性變數在該執行緒中的值

    void set(T value):設定此區域性變數在該執行緒中的值

    eg:  private ThreadLocal<String> name = new ThreadLocal();

           name.get();

           name.set("nameA");

           name.remove();

死鎖

當兩個執行緒互相等待釋放同步監視器時就會發生死鎖。

執行緒通訊

    1.synchronized關鍵字

    對於使用synchronized修飾的同步方法,該類預設例項(this)是同步監視器,可以直接呼叫這三個方法。

    對於使用synchronized修飾的同步程式碼塊,synchronized後的物件是同步監視器,該物件呼叫這三個方法。

    wait():導致當前執行緒等待,直到其他執行緒呼叫該同步監視器的notify()或者notifyAll()方法喚醒該執行緒

    notify():喚醒此同步監視器上等待的單個執行緒

    notifyAll():喚醒此同步監視器上等待的所有執行緒

    2.lock()

    await():類似wait()。導致當前執行緒等待,直到其他執行緒呼叫該Condition的signal()或者signalAll()方法喚醒該執行緒

    signal():喚醒此Lock物件上等待的單個執行緒

    signalAll():喚醒此Lock物件上等待的所有執行緒

使用阻塞佇列(BlockingQueue)控制執行緒通訊

    BlockingQueue繼承自Queue介面

    當生產者執行緒試圖向BlockingQueue放入元素,如果佇列已經滿了,則該執行緒被阻塞;當消費者執行緒試圖從BlockingQueue取出元素,如果佇列是空的,則該執行緒被阻塞。

    put(E e):嘗試把E元素放入BlockingQueue,如果佇列已經滿了,則該執行緒被阻塞

    take():從BlockingQueue取出元素,如果佇列是空的,則該執行緒被阻塞

    有以下五個實現類:

        ArrayBlockingQueue:基於陣列實現BlockingQueue

        LinkedBlockingQueue:基於連結串列實現BlockingQueue

        PriorityBlockingQueue:

        SynchronousQueue:同步佇列。對該佇列的存取操作必須交替進行

        DelayQueue:

執行緒組

    ThreadGroup表示執行緒組,可以對一批執行緒分類處理

    使用者建立的執行緒都屬於指定執行緒組,如果沒有顯示確定是哪個執行緒組,則屬於預設執行緒組。預設情況下,子執行緒與父執行緒屬於同一執行緒組。

    一旦執行緒加入指定執行緒組,執行緒執行中不可以改變執行緒組,直到死亡

    顯式指定執行緒組:

    Thread(ThreadGroup group,Runnable target)

    Thread(ThreadGroup group,Runnable target,String name)  //指定執行緒名字

    Thread(ThreadGroup group,String name)

    ThreadGroup建立例項

    ThreadGroup(String name)

    ThreadGroup(ThreadGroup parent, String name)

    int activeCount()  //返回此執行緒中活動執行緒的數目

    interrupt()    //中斷該執行緒組所有執行緒

    isDaemon()    //判斷該執行緒是否是後臺執行緒組

    setDaemon(boolean daemon)    //把該執行緒設定為後臺執行緒組

    setMaxPriority(int pri)        //設定執行緒組的最高優先順序

執行緒池

    返回ExecutorService物件,該物件代表一個執行緒池:

    newCachedThreadPool()建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。

    newFixedThreadPool(int nThreads) 建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。

    newSingleThreadExecutor() 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。

    返回ScheduledExecutorService物件,該物件代表一個執行緒池:

    newScheduledThreadPool(int corePoolSize) 建立一個定長執行緒池,支援定時及週期性任務執行。

    newSingleThreadScheduledExecutor 建立只有一個執行緒的執行緒池,可以在指定延時後執行執行緒任務

    ExecutorService newWorkStealingPool(int parallelism) 建立持有足夠執行緒的執行緒池來支援給定的並行級別,該方法還會使用多個佇列來減少競爭

    ExecutorService newWorkStealingPool(int parallelism) 前一個簡化版本,例如4個cpu,則並行級別設定為4

    ExecutorService代表儘快執行的執行緒池,只要有空閒執行緒就執行任務。

        Future<?> submit(Runnable task):將一個Runnable物件提交給指定執行緒池,在有空閒執行緒時執行。因為run()方法沒有返回值,所有該方法返回null

        <T>Future<T> submit(Runnable task, T result):將一個Runnable物件提交給指定執行緒池,在有空閒執行緒時執行。指定返回值,在執行結束後返回result

        <T>Future<T> submit(Callable<T> task):將一個Callable物件提交給指定執行緒池,在有空閒執行緒時執行,返回Future

    ScheduledExecutorService代表可在指定延遲後或者週期性執行執行緒任務的執行緒池。

        ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit):指定Callable任務在delay延時後執行

        ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit):指定command任務在delay延時後執行

        ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):指定command任務在delay延時後執行,以設定頻率重複執行

                執行:initialDelay initialDelay+peroid initialDelay+2 * peroid ....

        ScheduledFuture<?> scheduleAtFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):建立並執行一個在給定初始延遲後首次啟用的定期操作,隨後一次執行結束下次開始前都存在給定延時

包裝執行緒不安全的集合:

    使用Collections提供的類方法可以把執行緒不安全的集合包裝為執行緒安全

    <T> Collection<T> synchronizedCollections(Collections<T> c): 返回指定集合對應的執行緒安全的集合

    static <T> List<T> synchronizedList(List<T> list): 返回指定list物件對應的執行緒安全的list物件

    static <K,V> Map<K,V> synchronizedMap(Map<K,V> map): 返回指定Map物件對應的執行緒安全的Map物件

    static <T> Set<T> synchronizedSet(Set<T> set): 返回指定Set物件對應的執行緒安全的Set物件

    static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m): 返回指定SortedMap物件對應的執行緒安全的SortedMap物件

    static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s): 返回指定SortedSet物件對應的執行緒安全的SortedSet物件

    eg:HashMap m = Collections.synchronizedMap(new HashMap());  //把一個普通的HashMap包裝為執行緒安全的HashMap物件

執行緒安全的集合類:

    分為兩類:

    Concurrent開頭的:ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、ConcurrentLinkedQueue、ConcurrentLinkedDeque

    CopyOnWrite開頭的:CopyOnWriteArrayList、CopyOnWriteArraySet

    Concurrent開頭的表示支援併發訪問的集合,可以支援多執行緒併發寫入訪問,並且都是安全的,但讀取操作不必鎖定;CopyOnWrite開頭的底層通過複製實現,當對它操作時,實際是對陣列副本進行操作,因此執行緒安全。CopyOnWrite由於寫入需要頻繁複制陣列,效能比較差,但是在讀操作時,不需加鎖,操作很快很安全,適合讀取操作遠遠多於寫入操作的情況,比如快取。