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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

JDK并发包

發布時間:2024/4/17 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JDK并发包 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

JDK5之后引進了并發包java.util.concurrent,讓并發的開發更加可控,更加簡單。所以有必要好好學習下,下面從同步控制、并發容器、線程池三部分來詳細了解它。

1. 各種同步控制工具的使用

1.1 ReentrantLock(重用鎖)

1)與synchronized的區別是,它需要手動申請鎖與解鎖,在 finally 塊中釋放鎖,而synchronized是JVM自動處理的。可控性上ReentrantLock更強。

由于ReentrantLock是重入鎖,所以可以反復得到相同的一把鎖,它有一個與鎖相關的獲取計數器,如果擁有鎖的某個線程再次得到鎖,那么獲取計數器就加1,然后鎖需要被釋放兩次才能獲得真正釋放(重入鎖)。

注:synchronized 也是可重入的,當線程進入由線程已經擁有的監控器保護的 synchronized 塊,就允許線程繼續進行,當線程退出第二個(或者后續) synchronized 塊的時候,不釋放鎖,只有線程退出它進入的監控器保護的第一個synchronized 塊時,才釋放鎖,例如:

在帶有鎖的方法1里面調用了帶有鎖的方法2,這時在方法1獲得的鎖還是沒有釋放的,其它線程還是訪問不了方法1,即使方法2結束了,直至方法1的鎖釋放,鎖才會真正釋放。假如有多個線程同時來調用方法1,那輸出結果還是按順序輸出:1,2,1,2...

2)還有與synchronized不同的是,ReentrantLock對中斷是有響應的。普通的lock.lock()是不能響應中斷的,lock.lockInterruptibly()能夠響應中斷。

3)可限時,超時不能獲得鎖,就返回false,不會永久等待構成死鎖,使用lock.tryLock(long timeout, TimeUnit unit)來實現可限時鎖,參數為時間和單位。無法獲得后就直接退出了。

1.2 Condition

Condition與ReentrantLock的關系就類似于synchronized與Object.wait()/signal()

await()方法會使當前線程等待,同時釋放當前鎖,當其他線程中使用signal()時或者signalAll()方法時,線 程會重新獲得鎖并繼續執行。或者當線程被中斷時,也能跳出等待。這和Object.wait()方法很相似。

awaitUninterruptibly()方法與await()方法基本相同,但是它并不會再等待過程中響應中斷。 singal()方法用于喚醒一個在等待中的線程。相對的singalAll()方法會喚醒所有在等待中的線程。這和Obejct.notify()方法很類似。

1.3.Semaphore

對于鎖來說,它是互斥的排他的。意思就是,只要我獲得了鎖,沒人能再獲得了。

而對于Semaphore來說,它允許多個線程同時進入臨界區。可以認為它是一個共享鎖,但是共享的額度是有限制的,額度用完了,其他沒有拿到額度的線程還是要阻塞在臨界區外。當額度為1時,就相等于lock。??

常用方法有:

semaphore.acquire();//申請許可,當然一個線程也可以一次申請多個許可acquire(int permits),誰拿到令牌(acquire)就可以去執行了,如果沒有令牌則需要等待。

semaphore.release();//執行完畢,一定要歸還(release)令牌,否則令牌會被很快用光,別的線程就無法獲得令牌而執行下去了。

1.4 ReadWriteLock

ReadWriteLock是區分功能的鎖。讀和寫是兩種不同的功能,讀-讀不互斥,讀-寫互斥,寫-寫互斥。

這樣的設計是并發量提高了,又保證了數據安全。

使用方式是:

private static ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock(); private static Lock readLock = readWriteLock.readLock(); private static Lock writeLock = readWriteLock.writeLock();

1.5 CountDownLatch

倒數計時器,等待所有檢查線程全部完工后,再執行。每個任務完成后調用countDown(),計數器就會減1,當計數為0的時候,await()阻塞后面的方法會繼續執行。

1.6 CyclicBarrier

和CountDownLatch相似,也是等待某些線程都做完以后再執行。與CountDownLatch區別在于這個計數器可以反復使用。比如,假設我們將計數器設置為10。那么湊齊第一批1 0個線程后,計數器就會歸零,然后接著湊齊下一批10個線程。并且每次完成一批線程后會觸發一個動作

?CyclicBarrier(int parties, Runnable barrierAction)//barrierAction就是當計數器一次計數完成后,系統會執行的動作

也是通過await()來阻塞主線程等待任務全部完成

2. 并發容器

2.1?ConcurrentHashMap

我們知道HashMap不是一個線程安全的容器,最簡單的方式使HashMap變成線程安全就是使用Collections.synchronizedMap,它是對HashMap的一個包裝,如:

Collections.synchronizedMap(new HashMap())

同理對于List,Set也提供了相似方法。

但是這種方式只適合于并發量比較小的情況,它會將HashMap包裝在里面,然后將HashMap的每個操作都加上synchronized。

下面來看下ConcurrentHashMap是如何實現的:

在?ConcurrentHashMap內部有一個Segment段,它將大的HashMap切分成若干個段(小的HashMap),然后讓數據在每一段上Hash,這樣多個線程在不同段上的Hash操作一定是線程安全的,所以只需要同步同一個段上的線程就可以了,這樣實現了鎖的分離,大大增加了并發量。

在使用ConcurrentHashMap.size時會比較麻煩,因為它要統計每個段的數據和,在這個時候,要把每一個段都加上鎖,然后再做數據統計。這個就是把鎖分離后的小小弊端,但是size方法應該是不會被高頻率調用的方法。

2.2?BlockingQueue

BlockingQueue不是一個高性能的容器。但是它是一個非常好的共享數據的容器。是典型的生產者和消費者的實現。

它在內部實現了同步的隊列,實現方式采用的是我們第2種await() / signal()方法。它可以在生成對象時指定容量大小。它用于阻塞操作的是put()和take()方法。

put()方法:類似于我們上面的生產者線程,容量達到最大時,自動阻塞。

take()方法:類似于我們上面的消費者線程,容量為0時,自動阻塞。

3.線程池

線程池的作用就是將線程進行復用,減少創建與銷毀線程的開銷。

ThreadPoolExecutor是線程池的一個重要實現。

而Executors是一個工廠類。

新提交一個任務時的處理流程很明顯:

1、如果線程池的當前大小還沒有達到基本大小(poolSize < corePoolSize),那么就新增加一個線程處理新提交的任務;

2、如果當前大小已經達到了基本大小,就將新提交的任務提交到阻塞隊列排隊,等候處理workQueue.offer(command);

3、如果隊列容量已達上限,并且當前大小poolSize沒有達到maximumPoolSize,那么就新增線程來處理任務;

4、如果隊列已滿,并且當前線程數目也已經達到上限,那么意味著線程池的處理能力已經達到了極限,此時需要拒絕新增加的任務。至于如何拒絕處理新增

? ? 的任務,取決于線程池的飽和策略RejectedExecutionHandler。

3.1.線程池的種類

  • new FixedThreadPool 固定數量的線程池,線程池中的線程數量是固定的,不會改變。
  • new SingleThreadExecutor 單一線程池,線程池中只有一個線程。
  • new CachedThreadPool 緩存線程池,線程池中的線程數量不固定,會根據需求的大小進行改變。
  • new ScheduledThreadPool?計劃任務調度的線程池,用于執行計劃任務,比如每隔5分鐘怎么樣

3.2.拒絕策略

有時候,任務非常繁重,導致系統負載太大。在上面說過,當任務量越來越大時,任務都將放到FixedThreadPool的阻塞隊列中,導致內存消耗太大,最終導致內存溢出。這樣的情況是應該要避免的。因此當我們發現線程數量要超過最大線程數量時,我們應該放棄一些任務。丟棄時,我們應該把任務記下來,而不是直接丟掉。

共有以上4種策略。

AbortPolicy:如果不能接受任務了,則拋出異常。

CallerRunsPolicy:如果不能接受任務了,則讓調用的線程去完成。

DiscardOldestPolicy:如果不能接受任務了,則丟棄最老的一個任務,由一個隊列來維護。

DiscardPolicy:如果不能接受任務了,則丟棄任務。

在ThreadPoolExecutor的另一個構造函數中,可傳入一個handler,而handler就是拒絕策略的實現,它會告訴我們,如果任務不能執行了,該怎么做.

3.3 ForkJoin

Fork/Join框架是Java7提供了的一個用于并行執行任務的框架, 是一個把大任務分割成若干個小任務,最終匯總每個小任務結果后得到大任務結果的框架。有點類似工作流里面的分支合并概念

參考文章:

https://my.oschina.net/hosee/blog/485121

http://www.importnew.com/21288.html

https://my.oschina.net/hosee/blog/614319

3.4 Future

Future<V>代表一個異步執行的操作,通過get()方法可以獲得操作的結果,如果異步操作還沒有完成,則get()會使當前線程阻塞。FutureTask<V>實現了Future<V>和Runable<V>

3.5 Callable

一個有返回值的操作

3.6?CompletionService

CompletionService對ExecutorService進行了包裝,內部維護一個保存Future對象的BlockingQueue。只有當這個Future對象狀態是結束的時候,才會加入到這個Queue中,take()方法其實就是Producer-Consumer中的Consumer。它會從Queue中取出Future對象,如果Queue是空的,就會阻塞在那里,直到有完成的Future對象加入到Queue中。所以,先完成的必定先被取出。這樣就減少了不必要的等待時間。

與迭代了FutureTask的數組的區別是,CompletionService 任務完成后就把其結果加到result中,而不用依次等待每個任務完成。

轉載于:https://www.cnblogs.com/ptw-share/p/6681353.html

總結

以上是生活随笔為你收集整理的JDK并发包的全部內容,希望文章能夠幫你解決所遇到的問題。

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