線程JUC包(二)

  • 獲取線程的4種方式

1、繼承Thread類

2、實現Runnable 接口

3、實現Callable接口

4、用工具類Executors工具類或者new ThreadPoolExecute從線程池對象中獲取線程

  •  Lock鎖與synchronized鎖

Lock鎖實現了線程的有序調度,而synchronized由CPU隨機調度

多個線程下單,減庫存,發短信的有序進行,就需要用lock來完成,

那麼如果這個獲取鎖的線程由於要等待IO或者其他原因(比如調用sleep方法)被阻塞了,但是又沒有釋放鎖,其他線程便只能乾巴巴地等待,試想一下,這多麼影響程序執行效率。因此就需要有一種機制可以不讓等待的線程一直無期限地等待下去(比如只等待一定的時間或者能夠響應中斷),通過Lock就可以辦到。

Lock和synchronized有一點非常大的不同,採用synchronized不需要用戶去手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完之後,系統會自動讓線程釋放對鎖的佔用;而Lock則必須要用戶去手動釋放鎖,如果沒有主動釋放鎖,就有可能導致出現死鎖現象

  • Runnable與Callable不同點

1. Runnable不返回任務執行結果,Callable可返回任務執行結果

2. Callable在任務無法計算結果時拋出異常,而Runnable不能

3.Callable有泛型,Runnable沒有泛型

Runnable接口:public abstract void run();

Callable接口: V call() throws Exception;

Runnable的子接口的實現類與Callable接口有關係,Runnable 的子接口 RunnableFuture<v> 的實現類FutureTask 構造器中可以傳入Callable接口的實現類

 

  • executor包:

1、總綱接口:java.util.concurrent.Executor,只有一個抽象方法void execute(Runnable command);

2、總綱接口主要子接口:java.util.concurrent.ExecutorService,有方法submit()和shutdown() submit的參數是Callable<T>或者Runnable接口,返回值用Future<T>類型接收

3、延時調度子接口:java.util.concurrent.ScheduledExecutorService 爲 ExecutorService的子接口,有抽象方法:schedule()和shutdown() ,可以實現延時調用

4、線程池類:java.util.concurrent.ThreadPoolExecutor 繼承了父類 java.util.concurrent.AbstractExecutorService,父類實現了ExecutorService接口,線程池中有兩個執行方法:execute(沒有返回值),另一個是submit(返回一個Future<T>),以及shutdown()

  • locks包

1、java.util.concurrent.locks.Lock接口:有以下方法獲取鎖lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用來獲取鎖的

lock : 在鎖上等待,直到獲取鎖;必須主動去釋放鎖,並且在發生異常時,不會自動釋放鎖。因此一般來說,使用Lock必須在try{}catch{}塊中進行,並且將釋放鎖的操作放在finally塊中進行,以保證鎖一定被被釋放,防止死鎖的發生

tryLock:立即返回,獲得鎖返回true,沒獲得鎖返回false;或者根據入參等待指定時間後返回,必須用try{}catch{},在finally中釋放鎖,並且需要判斷lock.tryLock()方法是否返回true

2、java.util.concurrent.locks.ReentrantLock:爲Lock接口的實現類

3、讀寫鎖:java.util.concurrent.locks.ReadWriteLock接口,只有連個抽象方法:Lock readLock(),Lock writeLock()

4、讀寫鎖實現類:java.util.concurrent.locks.ReentrantReadWriteLock, 可進一步提高線程的併發性

獲取鎖:對象.readLock().lock(),對象.wirteLock().lock()

對於多個線程共享同一個資源的時候,多個線程同時對共享資源做讀操作是不會發生線程安全性問題的,但是一旦有一個線程對共享數據做寫操作其他的線程再來讀寫共享資源的話,就會發生數據安全性問題,所以出現了讀寫鎖ReentrantReadWriteLock

如果有線程想申請讀鎖的話,首先會判斷寫鎖是否被持有,如果寫鎖被持有且當前線程並不是持有寫鎖的線程,那麼就會返回-1,獲取鎖失敗,進入到等待隊列等待。如果寫鎖未被線程所持有或者當前線程和持有寫鎖的線程是同一線程的話就會開始獲取讀鎖

5、java.util.concurrent.locks.Condition,提供await(),singnal()與Lock鎖配套使用,示例:見上圖

java.util.concurrent.atomic 包中有類:Class AtomicInteger,Class AtomicLong等,定義了方法:getAndAdd,addAndGet,

確保了i++和++i的線程安全

  • 靜態方法鎖和普通方法鎖

1、一個對象裏面如果有多個synchronized方法,某一個時刻內,只要一個線程去調用其中的一個synchronized方法了,其它的線程都只能等待,換句話說,某一個時刻內,只能有唯一一個線程去訪問這些synchronized方法,鎖的是當前對象this,被鎖定後,其它的線程都不能進入到當前對象的其它的synchronized方法,換成兩個對象後,不是同一把鎖了,情況立刻變化

2、所有的非靜態同步方法用的都是同一把鎖——-實例對象本身,所有的靜態同步方法用的也是同一把鎖——-類對象本身,

這兩把鎖是兩個不同的對象,所以靜態同步方法與非靜態同步方法之間是不會有競態條件的

  • 鎖的相關概念

1、可重入鎖:像synchronized和ReentrantLock都是可重入鎖,當一個線程執行到某個synchronized方法時,比如說method1,而在method1中會調用另外一個synchronized方法method2,此時線程不必重新去申請鎖,而是可以直接執行方法method2

2、可中斷鎖:synchronized就不是可中斷鎖,而Lock是可中斷鎖,由於等待時間過長,線程不想等待了,想先處理其他事情,我們可以讓它中斷自己或者在別的線程中中斷它,這種就是可中斷鎖

3、公平鎖:儘量是線程按請求順序去執行

4、讀寫鎖:ReadWriteLock就是讀寫鎖,它是一個接口,ReentrantReadWriteLock實現了這個接口

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章