国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Java并發筆記(一)

2019-11-14 23:30:31
字體:
來源:轉載
供稿:網友
java并發筆記(一)

1. lock (todo)

2. 寫時復制容器

  CopyOnWrite容器即寫時復制的容器。通俗的理解是當我們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行Copy,復制出一個新的容器,然后新的容器里添加元素,添加完元素之后,再將原容器的引用指向新的容器。CopyOnWrite并發容器用于讀多寫少的并發場景。Java并發包里提供了兩個使用CopyOnWrite機制實現的并發容器, 它們是CopyOnWriteArrayList和CopyOnWriteArraySet (沒有map的寫時復制容器,可以自己實現).

  這樣做的好處是我們可以對CopyOnWrite容器進行并發的讀,而不需要加鎖,因為當前容器不會添加任何元素。所以CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不同的容器。

  CopyOnWrite容器有很多優點,但是同時也存在兩個問題,即內存占用問題和數據一致性問題。CopyOnWrite容器只能保證數據的最終一致性,不能保證數據的實時一致性。所以如果你希望寫入的的數據,馬上能讀到,請不要使用CopyOnWrite容器。

  http://ifeve.com/java-copy-on-write/

3. 線程安全容器分類

  • 同步容器,如Vector、Hashtable。這些類實現線程安全方式是:將它們的狀態封裝起來,并對每個公有方法都進行同步,使得每次只有一個線程能訪問容器的狀態。

    同步容器類雖然都是線程安全的,但在某些情況下可能需要額外的客戶端加鎖來保護復合操作。

  • 并發容器,如ConcurrentHashMap。并發容器用來改進同步容器的性能。同步容器將所有對容器狀態的訪問都串行化(包括讀),嚴重降低并發性。

    ConcurrentHashMap采用粒度更細的分段鎖來實現多個線程的并發訪問。

  • 寫時復制容器,如CopyOnWriteArrayList。在某些場合下用于替代同步容器,提供更好的并發性能,并且在迭代期間不需要對容器進行加鎖或復制。
  • 阻塞隊列,如BlockingQueue。阻塞隊列與普通隊列的區別在于,當隊列是空的時,從隊列中獲取元素的操作將會被阻塞,或者當隊列是滿時,往隊列里添加元素的操作會被阻塞。試圖從空的阻塞隊列中獲取元素的線程將會被阻塞,直到其他的線程往空的隊列插入新的元素。同樣,試圖往已滿的阻塞隊列中添加新元素的線程同樣也會被阻塞,直到其他的線程使隊列重新變得空閑起來,如從隊列中移除一個或者多個元素,或者完全清空隊列可用于多線程間協作運行,如生產者-消費者模型。比同步容器擁有更好的并發性能,因為當某線程進入synchronized代碼塊獲得鎖后,如果不滿足一定條件時,會執行wait()方法重新釋放鎖。

   http://ifeve.com/blocking-queues/ 

4. 線程中斷 interrupt

  Java曾經提供過搶占式中斷,但問題多多,例如的Thread.stop。另一方面,出于Java應用代碼的健壯性的考慮,降低了編程門檻,減少不清楚底層機制的程序員無意破壞系統的概率,這個問題很多,比如會破壞數據的完整性。

  如今,Java的線程調度不提供搶占式中斷,而采用協作式的中斷。其實,協作式的中斷,原理很簡單,就是輪詢某個表示中斷的標記,我們在任何普通代碼的中都可以實現。 例如下面的代碼:

volatile bool isInterrupted;    //…    while(!isInterrupted) {        compute();    }  

  interrupt就是這樣的一個通知,將Thead里的中斷標志位設為true,而線程能否退出,就看用戶的代碼對于這個通知是怎么處理的了。

  對于處于sleep,wait,join等操作的線程(即線程已被阻塞),如果被調用interrupt()后,會拋出InterruptedException,然后線程的中斷標志位會由true重置為false,因為線程為了處理異常已經重新處于就緒狀態。這其實是在sleep,wait,join這些方法內部會不斷檢查中斷狀態的值,而方法自己拋出的InterruptedException。

  實際上,JVM內部確實為每個線程維護了一個中斷標記。但應用程序不能直接訪問這個中斷變量,必須通過下面幾個方法進行操作:

public class Thread {      //設置中斷標記      public void interrupt() { ... }        //獲取中斷標記的值      public boolean isInterrupted() { ... }      //清除中斷標記,并返回上一次中斷標記的值      public static boolean interrupted() { ... }         ...  } 

  舉個栗子:

public class InterruptDemo extends Thread {        public static void main(String[] args) throws InterruptedException {          //創建線程          InterruptDemo sleep = new InterruptDemo();          //啟動線程          sleep.start();            //主線程中斷5秒鐘          Thread.sleep(5000);          //sleep子線程被中斷          sleep.interrupt();      }        public void run() {          try {              //做一個無限循環,每一秒打印一條信息              while (true) {                  sleep(1000);                  System.out.PRintln(getName() + " Is Running");              }          } catch (InterruptedException e) {              //被終端以后打印一條信息              System.out.println(getName() + " Is Interrupted");              return;          }      }  } 

  運行結果如下:

 

  OK,打印5次信息后成功被中斷。 

  對于以下代碼:

while(true){      try {       Thread.sleep(1000);      }catch(InterruptedException ex)      {            logger.error("thread interrupted",ex);      }   }  

  當線程執行sleep(1000)之后會被立即阻塞,如果在阻塞時外面調用interrupt來中斷這個線程,那么就會執行logger.error().

  這個時候其實線程并未中斷(因為拋出異常后中斷標志又重置為false),執行完這條語句之后線程會繼續執行while循環,開始sleep,所以說如果沒有對InterruptedException進行處理,后果就是線程可能無法中斷。

  所以,在任何時候碰到InterruptedException,都要手動把自己這個線程中斷。由于這個時候已經處于非阻塞狀態,所以可以正常中斷,最正確的代碼如下:

while(!Thread.isInterrupted()){      try {       Thread.sleep(1000);      }catch(InterruptedException ex)      {            Thread.interrupt()      }   }  

  這樣可以保證線程一定能夠被及時中斷。

  總之,如果線程沒有被阻塞,調用interrupt()將不起作用;否則,線程就將得到InterruptedException異常。

  http://blog.csdn.net/hudashi/article/details/6958550

  http://blog.csdn.net/lxcjie/article/details/8575169

  http://blog.csdn.net/srzhz/article/details/6804756

  http://www.ibm.com/developerworks/cn/java/j-jtp05236.html

5. 閉鎖 

  Latch閉鎖的意思,是一種同步的工具類。類似于一扇門:在閉鎖到達結束狀態之前,這扇門一直是關閉著的,不允許任何線程通過,當到達結束狀態時,這扇門會打開并允許所有的線程通過。且當門打開了,就永遠保持打開狀態。

  作用:可以用來確保某些活動直到其他活動都完成后才繼續執行。

  使用場景:

  1)例如我們上例中所有人都到達飯店然后吃飯;

  2)某個操作需要的資源初始化完畢

  3)某個服務依賴的線程全部開啟等等...

  CountDowmLatch是一種靈活的閉鎖實現,包含一個計數器,該計算器初始化為一個正數,表示需要等待事件的數量。countDown方法遞減計數器,表示有一個事件發生,而await方法等待計數器到達0,表示所有需要等待的事情都已經完成。

  http://blog.csdn.net/lmj623565791/article/details/26626391

6. 信號量Semaphore

  計數信號量用來控制同時訪問某個特定資源的操作數量,或者用來實現某種資源池,或者對容器施加邊界。

  可以用concurrent包中的Semaphore類來實現。當Semaphore的初始值為1時,可作為互斥量(mutex)。

  acquire()方法獲取Semaphore中的一個許可,如果沒有許可,則阻塞直到擁有許可。

  release()方法釋放Semaphore中的一個許可。

7. 柵欄 (CyclicBarrier, Exchanger)

  柵欄類似于閉鎖,它能阻塞一組線程直到某個事件發生。柵欄與閉鎖的關鍵區別在于,所有線程必須都到達柵欄位置,才能繼續執行。

  閉鎖用于等待事件,柵欄用于等待其他線程。

  例如,幾個家庭決定在某個地方集合,所有人在超市門口碰頭,之后再討論下一步要做的事。

  這里的人可以指代線程,線程沒有執行完,即大家都在等待其他人一起到達后再執行后面的操作。而閉鎖是指大家都已經執行完各自的線程(任務),某個領導人(另一事件) 等大家都到齊了準備開始后面的活動。

  http://blog.csdn.net/lmc_wy/article/details/7866863

8. Executor框架

  Executor框架是指java 5中引入的一系列并發庫中與executor相關的一些功能類,其中包括線程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。他們的關系為:

  

  并發編程的一種編程方式是把任務拆分為一些列的小任務,即Runnable(或Callable),然后在提交給一個Executor執行,Executor.execute(Runnalbe)。Executor在執行時使用內部的線程池完成操作。

  使用Executor來執行多個線程的好處是用來避免線程的創建和銷毀的開銷,以提升效率。因此如果某些場景需要反復創建線程去處理同類事務的話,可以考慮使用線程池來處理。

  • Executor接口只有一個execute(Runnable command)回調函數:
public interface Executor {    /**     * Executes the given command at some time in the future.  The command     * may execute in a new thread, in a pooled thread, or in the calling     * thread, at the discretion of the <tt>Executor</tt> implementation.     *     * @param command the runnable task     * @throws RejectedExecutionException if this task cannot be     * accepted for execution.     * @throws NullPointerException if command is null     */    void execute(Runnable command);}   
  • Executors類本身并不實現線程池的功能,只是提供了獲取ExecutorService的方法,而ExecutorService才是真正處理線程池相關邏輯的類。Executors下獲取ExecutorService的方法有很多,用于獲取各種不同的線程池,如單線程線程池、固定線程數的線程池等,不過最終還是調用ThreadPoolExecutor(ThreadPoolExecutor實現了ExecutorService)的構造函數來創建,如下:
public ThreadPoolExecutor(int corePoolSize,//最少線程數                          int maximumPoolSize,//最大線程數                          long keepAliveTime,//線程池滿后,后續線程的等待時間                          TimeUnit unit,//等待時間的單位                          BlockingQueue<Runnable> workQueue,//等待線程隊列                          ThreadFactory threadFactory)//線程生產工廠   

  通過以上方法就可以創建一個線程池方法,可以限制線程的數量和等待隊列中線程的等待時間等。然后如果要通過這個線程池來執行線程:

executorService.execute(new Runnable() {    @Override    public void run() {        System.out.println("Execute in pool:" + Thread.currentThread().getId());    }});

  通過execute()方法的執行是異步的,無法知道線程什么時候執行完畢。如果要想知道線程是否執行完畢,可以通過另外一個方法submit()來執行,然后獲取到一個future對象, 然后通過get()方法來判斷是否執行完畢:

Future<?> future = executorService.submit(new Runnable() {    @Override    public void run() {        try {            Thread.sleep(3000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("Execute in pool:" + Thread.currentThread().getId());    }});try {    if(future.get()==null){        System.out.println("finish!!!");    }} catch (InterruptedException e) { //get()方法阻塞時被中斷    e.printStackTrace();} catch (ExecutionException e) { //任務里發生了exception    e.printStackTrace();}

  但是通過這種方式只能知道線程是否執行完畢,卻做不到將各線程的處理結果返回做歸并處理。要實現這個目的可以使用Callable接口來封裝任務邏輯,Callable和Runable的 唯一區別就是它支持返回處理結果:

Future<?> future = executorService.submit(new Callable<String>() {    @Override    public String call() throws Exception {        return "hello callable!";    }});try {    System.out.println(future.get());} catch (InterruptedException e) {    e.printStackTrace();} catch (ExecutionException e) {    e.printStackTrace();}

  其中call()方法中返回的值,就是Future對象get()到的值。但是如果有多個線程在處理,然后要將這些線程的處理結果歸并怎么做呢?當然可以使用ExecutorService來獲取每個放到線程池的線程的Future對象,然后遍歷的去get()然后去做歸并處理。但是顯然這種方法并不能做到先完成的就被先歸并,而是取決于遍歷到的時間,這顯然降低了處理效率。要處理這種場景,可以使用另外一個Service–ExecutorCompletionService(ExecutorCompletionService繼承自CompletionService):

public interface CompletionService<V> {    Future<V> submit(Callable<V> task);    Future<V> submit(Runnable task, V result);  
  //Retrieves and removes the Future representing the next completed task, waiting if none are yet present.    Future<V> take() throws InterruptedException; 
    //Retrieves and removes the Future representing the next completed task or <tt>null</tt> if none are present.    Future<V> poll();    Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;}

public class ExecutorCompletionService<V> implements CompletionService<V> { // ExecutorCompletionService 組合了 Execute 和 BlockingQueue    private final Executor executor;    private final AbstractExecutorService aes;    private final BlockingQueue<Future<V>> completionQueue;    public ExecutorCompletionService(Executor executor) {        if (executor == null)            throw new NullPointerException();        this.executor = executor;        this.aes = (executor instanceof AbstractExecutorService) ?            (AbstractExecutorService) executor : null;        this.completionQueue = new LinkedBlockingQueue<Future<V>>();    }        ...  }

ExecutorService executorService = Executors.newFixedThreadPool(4);CompletionService<Long> completionService = new ExecutorCompletionService<Long>(executorService);for (int i = 0; i < 4; i++) {    long sleep = (5 - i) * 1000;    completionService.submit(new ExeWorker(sleep));}for(int i=0;i<4;i++){    try {        System.out.println(completionService.take().get()+" Get!");    } catch (InterruptedException e) {        e.printStackTrace();    } catch (ExecutionException e) {        e.printStackTrace();    }}class ExeWorker implements Callable<Long> {    private long sleep;    public ExeWorker(long sleep) {        this.sleep = sleep;    }    @Override    public Long call() throws Exception {        System.out.println(sleep + " Executing!");        Thread.sleep(sleep);        System.out.println(sleep + " Done!");        return sleep;    }}

  以線程的sleep時間為線程名稱,然后輸出結果為:

5000 Executing!4000 Executing!3000 Executing!2000 Executing!2000 Done!2000 Get!3000 Done!3000 Get!4000 Done!4000 Get!5000 Done!5000 Get!

  可以看出后面那個循環獲取處理結果的地方的確是按先完成先返回的方式來實現。這種方法的一個約束就是需要知道有多少個線程在處理。其實CompletionService底層是通過一個BlockingQueue來存放處理結果,你也可以使用它自身封裝好的帶超時的poll方法來獲取返回結果。

   

  http://m.survivalescaperooms.com/topic/366591

  http://blog.csdn.net/yanhandle/article/details/9037401

  http://blog.csdn.net/linghu_java/article/details/17123057

9.ThreadLocal(todo)

10.ThreadPoolExecutor (todo)

  http://blog.csdn.net/wangwenhui11/article/details/6760474

  http://blog.csdn.net/cutesource/article/details/6061229

  http://dongxuan.VEvb.com/blog/901689


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 丰顺县| 将乐县| 叶城县| 革吉县| 山丹县| 宝山区| 桐庐县| 金堂县| 沧州市| 勐海县| 阿拉善盟| 定南县| 湖北省| 新田县| 漯河市| 安庆市| 怀仁县| 华阴市| 凤凰县| 穆棱市| 筠连县| 霍邱县| 永胜县| 郧西县| 湄潭县| 准格尔旗| 山东省| 永新县| 昭苏县| 永靖县| 呼图壁县| 太白县| 柳州市| 黄龙县| 颍上县| 洪泽县| 金山区| 宜阳县| 丰台区| 营山县| 高阳县|