1. 程式人生 > >JDK併發相關併發包

JDK併發相關併發包

    synchronized的功能擴充套件:

    重入鎖;ReentrantLock;

        其實也就是lock對於synchronized的替代了,要注意的一個點就是你可以鎖多個,但是你鎖了幾個,最後就要解鎖幾個這個問題;

        使用lock.lock()加鎖,使用lock.unlock()解鎖;

    提供中斷處理;

  使用中斷鎖,執行緒可以被中斷,也就是說,當一個執行緒正在等待鎖的時候,他依然可以收到一個通知,被告知無須等待,可以停止工作了,使用的是lock.lockInterruptibly();方法;

    鎖申請等待限時;

  給鎖給定一個時間,如果超過了這個時間的話,就讓執行緒自動放棄;使用的是lock.trylock(時間段,時間單位);另外還有一個就是lock.trylock();如果是帶引數的,就是最多等待這麼多時間,超過了時間就返回false,成功獲得了鎖,就返回true;如果是不帶引數直接執行,就是比較直接的,如果鎖未被佔用,則申請鎖成功,返回true,否則就是失敗了,直接返回false;

    公平鎖;

        公平鎖的一個特點就是:它不會產生飢餓現象;所有的鎖都是先到先等,不得插隊;但是維持公平需要一個有序佇列,實現成本較高,效能相對也非常低下,預設情況下,鎖是非公平的;

    以下是我們整理的幾個重要方法:

        1,lock():獲得鎖,如果鎖已經被佔用,則等待;         2,lockInterruptibly():獲得鎖,但優先響應中斷;         3,tryLock():嘗試獲得鎖,如果成功,返回true,失敗返回false,該方法不等待,立即返回;         4,tryLock(Long time,TimeUnit unit):在給定的時間內嘗試獲得鎖;         5,unlock():釋放鎖;

    重入鎖的好搭檔:Condition條件;

使用new Condition可以生成與當前重入鎖繫結的Condition例項;使得我們可以讓執行緒在合適的時間等待,或者在某一特定的時刻得到通知,繼續執行;         1,await();讓當前執行緒等待,同時釋放當前鎖,作用上和wait()相似;         2,awaitUninterruptibly();與await()方法基本相同,不會在等待過程中響應中斷;         3,singal();用於喚醒一個在等待中的執行緒,相對的singalAll()方法會喚醒所有在等待中的執行緒,和notify()類似;

    允許多個執行緒同時訪問:訊號量;

        只允許一個執行緒訪問一個資源,而訊號量則可以指定多個執行緒;         public Semaphore(int permits)         public Semaphore(int permits,boolean fair);第二個引數可以指定是否公平鎖;         這所謂的訊號量,其實就是一個執行器啦,只是換了名字看不出來了而已。。。引數可以指定有多少個執行緒;

    ReadWriteLock讀寫鎖;

關於讀寫的問題,我們要明確的一點就是,我們可以對一個檔案多次重複讀取,但是當讀與寫發生衝突的時候,我們要做的就是保證他們的互斥了;         讀讀不互斥;         讀寫互斥;         寫寫互斥;     如果讀的操作遠遠大於寫的操作的話,讀寫鎖就會發揮最大的功效;     Lock readLock = readWriteLock.readLock();     Lock writeLock = readWriteLock.writeLock();     讀執行緒完全並行,而寫會阻塞讀;

    倒計時器:CountDownLatch;

        此工具通常用來控制執行緒等待,讓某一個執行緒等待知道倒計時結束,再開始執行;         只有當你一開始設定的所有執行緒都跑完了,你這個倒計時器才算真正結束了;         CountDownLatch end = new CountDownLatch(10);         end.countDown();               當呼叫CountDownLatch.countDown()方法的時候,也就是通知CountDownLatch,一個執行緒已經完成了任務,倒計時器可以減1了;

    迴圈珊欄;CyclicBarrier;

        另外一種多執行緒併發控制實用工具;             其實本質上跟倒計時器是差不多的功能類似,唯一的區別就在於可以反覆使用,而且可以設定,當計數結束之後,系統下一步要執行的動作;         public CyclicBarrier(int parties,Runnable barrierAction);             其中barrierAction是執行的動作;

    執行緒阻塞工具類:LockSupport;

        可以線上程內任意位置讓執行緒阻塞;和Thread.suspend()相比,彌補了由於resume()在前發生,導致執行緒無法繼續執行的情況;         與object.wait()相比,不需要先獲得某個物件的鎖,也不會丟擲interruptedException異常;         LockSupport.park();         LockSupport.unpark(t1);        除了有定時阻塞的功能外,LockSupport.park()還能支援中斷影響;但是LockSuppor.park()不會丟擲InterruptedException異常,他只是默默的返回;          也就是說:LockSupport.park()進行阻塞之後,如果中斷這個執行緒,不會丟擲異常,而是默默的返回;

    執行緒複用:執行緒池;

        我們在這裡用到了池的概念來儲存管理我們的執行緒;         當我們要使用執行緒的時候,不是建立,而是從池子中去取,當我們要關閉執行緒的時候,是將這個執行緒返回給池子,方便其他人使用;

    JDK對執行緒池的支援;

         newFixedThreadPool(int nThreads);          newSingleThreadExecutor();          newCachedThreadTool();          newSingleThreadScheduledExector();          newScheduledThreadPool(int corePoolSize);     newFixedThreadPool():該方法返回一個固定執行緒數量的執行緒池;執行緒池的數量保持不變,若執行緒池已滿,新的任務會被暫存在一個任務佇列中;     newSingleThreadExecutor();該執行緒池只有一個執行緒在裡面,多餘任務會被儲存到一個任務佇列中;     newCachedThreadPool();返回一個根據實際情況調整執行緒數量的執行緒池,也就是說,這個執行緒池中的執行緒是可以可調整的。當所有的執行緒都在工作而此時又有新的任務提交,則會擴充套件執行緒的數量;     newSingleThreadScheduledExecutor();返回一個ScheduledExecutorService物件,在ExecutorService介面上擴充套件了在給定時間執行某任務的功能,     newScheduledThreadPool()方法,可以指定執行緒數量和時間的執行緒池;

    核心執行緒池的內部實現;

        都是在原有的ThreadPoolExecutor()的基礎上進行的引數設定和修改;         而其實,這個ThreadPoolExecutor()內部的引數也有很多的;         int corePoolSize;執行緒池中執行緒的數量;         int maximumPoolSize;指定了執行緒池的最大執行緒數量;         long keepAliveTime;當執行緒池數量超過了corePoolSize時嗎,多與的空閒執行緒的存活時間;         TimeUnit unit;keepAliveTime的時間單位;         BlockingQueue<Runnable> workQueue:任務佇列,被提交但尚未被執行的任務;         ThreadFactory threadFactory;執行緒工廠,用於建立執行緒,一般用預設的;         handler;拒絕策略,當任務太多來不及處理,如何拒絕任務;     現在我們上面的幾個執行緒池是如何通過ThreadPoolExecutor()以及內部的引數來設定的呢,我們現在就來說一下;         其中newFixedThreadPool(nThreads,nThreads,0L,new LinkedBlockingQueue<Runnable>);         newSingleThreadExecutor(1,1,0L,new LinkedBlockingQueue<Runnable>);         newCachedThreadPool(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue<Runnable>());         其中,我們可以看出,當newCachedThreadPool時,我們是指定執行緒池數量為0,但是最大執行緒數為無限,而且採用的任務佇列跟上面的佇列不一樣,下面將重點說明這兩種任務佇列的不同之處;         SynchronousQueue:這是直接提交的佇列,每一個執行緒都會直接提交,不會真實地儲存,而是直接將新任務交給執行緒執行器;         ArrayBlockingQueue:這是有界的任務佇列,需要帶一個引數,表示該佇列的最大容量;         LinkedBlockingQueue:這是無界的任務佇列,多餘的任務會先放到這裡;         PriorityBlockingQueue:這是優先任務佇列,根據任務自身的優先順序順序先後執行;

        不要重複發明輪子:JDK的併發容器;

            ConcurrentHashMap:高效的併發HashMap;             CopyOnWriteArrayList:在讀多寫少的場合,這個List的效能非常好,遠遠好於Vector(另外。。Vector已經過時了,不用了);             ConcurrentLinkedQueue:高效的併發佇列,使用連結串列實現,可以看做一個執行緒安全的LinkedList;             BlockingQueue:表示阻塞佇列,詳情可以檢視一下生產者——消費者模式;             ConcurrentSkipListMap:跳錶的實現;這是一個Map,使用跳錶的資料結構進行快速查詢;         以上併發容器的內部資料結構實現,我們在下一章會提到;敬請期待!