日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java并发编程实战阅读总结(a)

發布時間:2023/12/20 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java并发编程实战阅读总结(a) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1、鎖(lock)與volatile
(1)、隱式鎖,java提供了強制原子性的內置鎖機制:synchronized塊或synchronized方法。
操作共享狀態的復合操作必須是原子的,以避免競態條件,比如讀-改-寫操作和檢查再運行操作。
復合操作會在完整的運行期占有鎖,以確保其行為為原子的。
不管是同步代碼塊還是同步方法,每次只有一個線程可以進入,如果其他線程試圖進入(不管是同一同步塊還是不同的同步塊),
JVM會將它們掛起(放入到等鎖池中)。這種結構在并發理論中稱為臨界區。
這里我們可以對Java中用synchronized實現同步和鎖的功能做一個總結

靜態同步方法會鎖定它的Class對象
同步方法可以視為包含整個方法的synchronized(this) { … }代碼塊
synchronized修飾符并不是方法簽名的組成部分,所以不能出現在接口的方法聲明中
非同步的方法不關心鎖的狀態,它們在同步方法運行時仍然可以得以運行
synchronized實現的鎖是可重入的鎖。

在JVM內部,為了提高效率,同時運行的每個線程都會有它正在處理的數據的緩存副本,當我們使用synchronzied進行同步的時候,真正被同步的是在不同線程中表示被鎖定對象的內存塊簡單的說就是在同步塊或同步方法執行完后,對被鎖定的對象做的任何修改要在釋放鎖之前寫回到主內存中;在進入同步塊得到鎖之后,被鎖定對象的數據是從主內存中讀出來的,持有鎖的線程的數據副本一定和主內存中的數據視圖是同步的

(2)、volatile關鍵字:volatile修飾變量或對象,被volatile修飾的變量或對象告訴vm從內存中讀取該變量、對象的值

變量的值在使用之前總會從主內存中再讀取出來。

對變量值的修改總會在完成之后寫回到主內存中。


volatile不能保證多線程中的同步,但是可以保證在多線程中數據是可見的。


(3)、顯式鎖 Lock 和 ReentrantLock
與內置鎖機制不同的是,Lock提供了一種無條件的、可輪詢的、定時的以及可中斷的鎖獲取操作,所有加鎖和解鎖方法都是顯示的。
lock能實現synchronized實現的所有功能,lock()的使用更加方便靈活、功能更加強大。

lock分別為讀、寫提供了鎖,這樣效率更高。
public interface Lock {
? ? //獲取鎖
? ? void lock();
? ? //如果當前線程未被中斷,則獲取鎖。
? ? void lockInterruptibly() throws InterruptedException;
? ? //僅在調用時鎖為空閑狀態才獲取鎖
? ? tryLock();
? ? //如果鎖在給定的等待時間內空閑,并且當前線程未被中斷,則獲取鎖。
? ? tryLock(long time,TimeUnit unit);
? ? //返回綁定此Lock實例的新Condition實例。
? ? Condition newCondition();
}lock方法獲取不到鎖則阻塞,tryLock獲取不到鎖不阻塞直接返回false。


lock帶來靈活、方便的同時也引入隱患就是死鎖,lock的釋放unlock需要在finally代碼塊里釋放鎖,如果沒有釋放鎖很可能程序運行不下去,造成死鎖。

Lock lock = new ReentrantLock(); lock.lock(); try {// update object state } finally {lock.unlock(); } 必須在finally 中來釋放Lock
這也是使用synchronized比使用lock簡單的一個點。

synchronized和ReentrantLock之間的選擇:
ReentrantLock在加鎖和內存上提供的語義與內置鎖相同,此外它還提供了一些其他功能,包括定時等待,可中斷的鎖等待,公平性,以及實現非塊結構的加鎖。
同時ReentrantLock為讀、寫分別提供了鎖機制,強制的進行了分離。
然而ReentrantLock的危險性比同步機制要高,如果忘記在finally塊中調用unlock,那么雖然代碼表面上正常運行,
但實際上已經埋下了一顆定時炸彈,并可能傷及其他代碼。僅內置鎖不能滿足需求時,才可以考慮使用ReentrantLock.
在一些內置鎖無法滿足需求時,ReentrantLock可以作為一種高級工具,當需要一些高級功能時才應該使用ReentrantLock,
這些功能包括:可定時的、可輪詢的與可中斷的鎖獲取操作,公平隊列,以及非塊結構的鎖。
否則,還應該優先使用synchronized。ReentrantLock的非塊結構仍然意味著,獲取鎖的操作不能與特定的棧幀關聯起來,而內置鎖則可以。

(4)、鎖的喚醒:

notify 和 notifyAll:
在條件隊列API中有兩個發出通知的方法,即 notify和 notifyAll。無論調用哪一個,都必須持有與條件隊列對象相關聯的鎖。notify操作會選在條件隊列中的一個線程喚醒,其他線程則無法得到喚醒通知。而notifyAll則會喚醒所有等待在條件隊列中的線程來競爭鎖。使用notify單一的同時可能發生信號丟失問題。
普遍認可的做法是優先使用notifyAll而不是notify。雖然notifyAll可能比notify更低效,但卻更容易確保類的行為是正確的。

注意:notify()對于只有一個線程時是有意義的,多線程時需要使用notifyAll(),由于notify()和notifyAll()不精確,因此建議使用信號量或阻塞隊列來實現對共享資源的加鎖。

2、并發集合ConcurrentHashMap

先說下HashMap:其是線程不安全的,沒有同步的機制,在多線程執行時可能會造成數據的錯亂,并且效率也低。HashMap底層實現機制是一個數組,數組中的每個元素又是一個單鏈表,只有hashcode值沖突的元素才放到這個單鏈表中。HashMap中的元素是由key與value封裝成的Entity對象,這是造成其占用很大內存空間的根本原因,其次是他的默認大小是16,在需要增長時增長到原來的2倍,并且還是2的N次方。

ConcurrentHashMap即使線程安全的又是高效的。通過源碼分析他的底層實現機制是一個segment數組,即段數組。將數組中的元素分為若干段,每一段對應一個鎖,這可以達到鎖分離的目的,對其中一段的訪問不影響其他的元素,提交了效率。他的并發度是16,是構造器中的一個參數。


3、通過共享對象達到共享數據的目的
(1)、設計線程安全的類。
? ? ?找出構造對象狀態的所有變量。
? ? ?約束狀態變量的不變性條件。
? ? ?建立對象狀態的并發訪問管理策略。
(2)、實例封閉
? ? ?如果某對象不是線程安全的,我們可以通過多種技術使其在多線程中能安全的使用。確保該對象只能由單個線程訪問。
public class AnimalSet{ ?
? ? ? ? ?private final Set<Animal> mySet = new HashSet<Animal>(); ?
? ? ? ? ? ?
? ? ? ? ?public sychronized void addAnimaln(Animal p) { ?
? ? ? ? ? ? ? mySet.add(p) ?
? ? ? ? ?} ?
? ? ??
? ? ? ? ?public sychronized boolean containsAnimal(Animal p) { ?
? ? ? ? ? ? ? return mySet.contains(p); ?
? ? ? ? ?} ?
? ? } ?


雖然HashSet并非線程安全的。但是mySet是私有的不會逸出。唯一能訪問mySet的代碼是addPerson()和containsPerson()。
在執行上他們都要獲的PersonSet 上的鎖。PersonSet的狀態完全又它的內置鎖保護。所以AnimalSet是一個線程安全的類。


4、基礎模塊


(1)、同步容器類。線程安全的容器包括Vector和Hashtable。同步的封裝容器類由Collections.sychronizedXXX工廠方法創建。
? ? ?eg:synchronizedList,synchronizedMap(m)、synchronizedSet(s)這些是通過synchronized同步方法來實現的,達到了線程安全但是效率低
(2)、同步工具類。
? ? ?阻塞隊列(BlockingQueue(LinkedBlockingQueue,ArrayBlockingQueue,PriorityBlockingQueue,SynchronousQueue))不僅可以保存對象的容器,而且還可以協調生產者和消費者之間的控制流。
? ? ?信號量(Semaphore):用來控制同時訪問某個特定資源的操作數量。通過 acquire() 獲取一個許可,如果沒有就等待,而 release() 釋放一個許可。Semaphore允許線程獲取許可, 未獲得許可的線程需要等待.這樣防止了在同一時間有太多的線程執行。Semaphore實現的功能就類似廁所有5個坑,假如有10個人要上廁所,那么同時只能有多少個人去上廁所呢?同時只能有5個人能夠占用,當5個人中 的任何一個人讓開后,其中等待的另外5個人中又有一個人可以占用了。另外等待的5個人中可以是隨機獲得優先機會,也可以是按照先來后到的順序獲得機會,這取決于構造Semaphore對象時傳入的參數選項。單個信號量的Semaphore對象可以實現互斥鎖的功能,并且可以是由一個線程獲得了“鎖”,再由另一個線程釋放“鎖”,這可應用于死鎖恢復的一些場合。
?eg:模擬30輛車去泊車,而車位有10個的場景. 當車位滿時,出來一輛車,才能有一輛車進入停車.

轉載 http://mouselearnjava.iteye.com/blog/1921468

package my.concurrent.semaphore; import java.util.concurrent.Semaphore; public class Car implements Runnable { private final Semaphore parkingSlot; private int carNo; /** * @param parkingSlot * @param carName */ public Car(Semaphore parkingSlot, int carNo) { this.parkingSlot = parkingSlot; this.carNo = carNo; } public void run() { try { parkingSlot.acquire(); parking(); sleep(300); parkingSlot.release(); leaving(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void parking() { System.out.println(String.format("%d號車泊車", carNo)); } private void leaving() { System.out.println(String.format("%d號車離開車位", carNo)); } private static void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } package my.concurrent.semaphore; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; public class ParkingCars { private static final int NUMBER_OF_CARS = 30; private static final int NUMBER_OF_PARKING_SLOT = 10; public static void main(String[] args) { /* * 采用FIFO, 設置true */ Semaphore parkingSlot = new Semaphore(NUMBER_OF_PARKING_SLOT, true); ExecutorService service = Executors.newCachedThreadPool(); for (int carNo = 1; carNo <= NUMBER_OF_CARS; carNo++) { service.execute(new Car(parkingSlot, carNo)); } sleep(3000); service.shutdown(); /* * 輸出還有幾個可以用的資源數 */ System.out.println(parkingSlot.availablePermits() + " 個停車位可以用!"); } private static void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

運行結果: 1號車泊車?
4號車泊車?
9號車泊車?
2號車泊車?
8號車泊車?
10號車泊車?
3號車泊車?
12號車泊車?
14號車泊車?
6號車泊車?
2號車離開車位?
4號車離開車位?
6號車離開車位?
1號車離開車位?
9號車離開車位?
3號車離開車位?
5號車泊車?
8號車離開車位?
10號車離開車位?
11號車泊車?
7號車泊車?
12號車離開車位?
13號車泊車?
14號車離開車位?
16號車泊車?
17號車泊車?
20號車泊車?
19號車泊車?
18號車泊車?
15號車泊車?
5號車離開車位?
20號車離開車位?
18號車離開車位?
22號車泊車?
11號車離開車位?
7號車離開車位?
13號車離開車位?
15號車離開車位?
21號車泊車?
26號車泊車?
23號車泊車?
28號車泊車?
25號車泊車?
16號車離開車位?
27號車泊車?
17號車離開車位?
30號車泊車?
24號車泊車?
29號車泊車?
19號車離開車位?
25號車離開車位?
24號車離開車位?
22號車離開車位?
26號車離開車位?
28號車離開車位?
30號車離開車位?
21號車離開車位?
23號車離開車位?
27號車離開車位?
29號車離開車位?
10 個停車位可以用!

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的java并发编程实战阅读总结(a)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。