<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    qileilove

    blog已經轉移至github,大家請訪問 http://qaseven.github.io/

    Java線程并發控制基礎知識

     線程池
      推薦用ThreadPoolExecutor的工廠構造類Executors來管理線程池,線程復用線程池開銷較每次申請新線程小,具體看代碼以及注釋
    public class TestThread {
    /**
    * 使用線程池的方式是復用線程的(推薦)
    * 而不使用線程池的方式是每次都要創建線程
    * Executors.newCachedThreadPool(),該方法返回的線程池是沒有線程上限的,可能會導致過多的內存占用
    * 建議使用Executors.newFixedThreadPool(n)
    *
    * 有興趣還可以看下定時線程池:SecheduledThreadPoolExecutor
    */
    public static void main(String[] args) throws InterruptedException, ExecutionException {
    int nThreads = 5;
    /**
    * Executors是ThreadPoolExecutor的工廠構造方法
    */
    ExecutorService executor = Executors.newFixedThreadPool(nThreads);
    //submit有返回值,而execute沒有返回值,有返回值方便Exception的處理
    Future res = executor.submit(new ConsumerThread());
    //executor.execute(new ConsumerThread());
    /**
    * shutdown調用后,不可以再submit新的task,已經submit的將繼續執行
    * shutdownNow試圖停止當前正執行的task,并返回尚未執行的task的list
    */
    executor.shutdown();
    //配合shutdown使用,shutdown之后等待所有的已提交線程運行完,或者到超時。繼續執行后續代碼
    executor.awaitTermination(1, TimeUnit.DAYS);
    //打印執行結果,出錯的話會拋出異常,如果是調用execute執行線程那異常會直接拋出,不好控制,submit提交線程,調用res.get()時才會拋出異常,方便控制異常
    System.out.println("future result:"+res.get());
    }
    static class ConsumerThread implements Runnable{
    @Override
    public void run() {
    for(int i=0;i<5;i++) {
    System.out.println(i);
    }
    }
    }
    }
      輸出:
      0
      1
      2
      3
      4
      future result:null

    線程同步
      synchronized(this)和synchronized(MyClass.class)區別:前者與加synchronized的成員方法互斥,后者和加synchronized的靜態方法互斥
      synchronized的一個應用場景是單例模式的,雙重檢查鎖
    public class Singleton {
    private volatile static Singleton singleton;
    private Singleton (){}
    public static Singleton getSingleton() {
    if (singleton == null) {
    synchronized (Singleton.class) {
    if (singleton == null) {
    singleton = new Singleton();
    }
    }
    }
    return singleton;
    }
    }
      注意:不過雙重檢查鎖返回的實例可能是沒有構造完全的對象,高并發的時候直接使用有問題,不知道在新版的java里是否解決了
      所以有了內部類方式的單例模式,這樣的單例模式有了延遲加載的功能(還有一種枚舉方式的單例模式,用的不多,有興趣的可以上網查)
    //(推薦)延遲加載的單例模式
    public class Singleton {
    private static class SingletonHolder {
    private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static final Singleton getInstance() {
    return SingletonHolder.INSTANCE;
    }
    }
      若不要延遲加載,在類加載的時候實例化對象,那直接這么寫,如下:
    public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton (){}
    public static Singleton getInstance() {
    return instance;
    }
    }
      volatile保證同一變量在多線程中的可見性,所以它更多是用于修飾作為開關狀態的變量
      用synchronized修飾變量的get和set方法,不但可以保證和volatile修飾變量一樣的效果(獲取最新值),因為synchronized不僅會把當前線程修改的變量的本地副本同步給主存,還會從主存中讀取數據更新本地副本。而且synchronized還有互斥的效果,可以有效控制并發修改一個值,因為synchronized保證代碼塊的串行執行。如果只要求獲取最新值的特性,用volatile就好,因為volatile比較輕量,性能較好
    .
     互斥鎖、讀寫鎖
      ReentrantLock 和 ReentrantReadWriteLock
      JDK5增加了ReentrantLock這個類因為兩點:
      1.ReentrantLock提供了tryLock方法,tryLock調用的時候,如果鎖被其他線程(同一個線程兩次調用tryLock也都返回true)持有,那么tryLock會立即返回,返回結果是false。lock()方法會阻塞。
      2.構造RenntrantLock對象可以接收一個boolean類型的參數,描述鎖公平與否的函數。公平鎖的好處是等待鎖的線程不會餓死,但是整體效率相對低一些;非公平鎖的好處是整體效率相對高一些。
      注意:使用ReentrantLock后,需要顯式地進行unlock,所以建議在finally塊中釋放鎖,如下:
    lock.lock();
    try {
    //do something
    }
    finally {
    lock.unlock();
    }
      ReentrantReadWriteLock與ReentrantLock的用法類似,差異是前者通過readLock()和writeLock()兩個方法獲得相關的讀鎖和寫鎖操作。
      原子數
      除了用互斥鎖控制變量的并發修改之外,jdk5中還增加了原子類,通過比較并交換(硬件CAS指令)來避免線程互斥等待的開銷,進而完成超輕量級的并發控制,一般用來高效的獲取遞增計數器。
      AtomicInteger counter = new AtomicInteger();
      counter.incrementAndGet();
      counter.decrementAndGet();
      可以簡單的理解為以下代碼,增加之后與原先值比較,如果發現增長不一致則循環這個過程。代碼如下
    public class CasCounter {
    private SimulatedCAS value;
    public int getValue() {
    return value.getValue();
    }
    public int increment() {
    int oldValue = value.getValue();
    while (value.compareAndSwap(oldValue, oldValue + 1) != oldValue)
    oldValue = value.getValue();
    return oldValue + 1;
    }
    }
      可以看IBM工程師的一篇文章 Java 理論與實踐: 流行的原子
     喚醒、通知
      wait,notify,notifyAll是java的Object對象上的三個方法,多線程中可以用這些方法完成線程間的狀態通知。
      notify是喚醒一個等待線程,notifyAll會喚醒所有等待線程。
      CountDownLatch主要提供的機制是當多個(具體數量等于初始化CountDownLatch時的count參數的值)線程都到達了預期狀態或完成預期工作時觸發事件,其他線程可以等待這個事件來觸發后續工作。
      舉個例子,大數據分拆給多個線程進行排序,比如主線程
    CountDownLatch latch = new CountDownLatch(5);
    for(int i=0;i<5;i++) {
    threadPool.execute(new MyRunnable(latch,datas));
    }
    latch.await();
    //do something 合并數據
      MyRunnable的實現代碼如下
    public void run() {
    //do something數據排序
    latch.countDown();
    //繼續自己線程的工作,與CyclicBarrier最大的不同,稍后馬上講
    }
      CyclicBarrier循環屏障,協同多個線程,讓多個線程在這個屏障前等待,直到所有線程都到達了這個屏障時,再一起繼續執行后面的動作。
      使用CyclicBarrier可以重寫上面的排序代碼
      主線程如下
    CyclicBarrier barrier = new CyclicBarrier(5+1); //主線程也要消耗一個await,所以+1
    for(int i=0;i<5;i++) {
    threadPool.execute(new MyRunnable(barrier,datas));//如果線程池線程數過少,就會發生死鎖
    }
    barrier.await();
    //合并數據
      MyRunnable代碼如下
      public void run() {
      //數據排序
      barrier.await();
      }
      //全部 count+1 await之后(包括主線程),之后的代碼才會一起執行
      信號量
      Semaphore用于管理信號量,與鎖的最大區別是,可以通過令牌的數量,控制并發數量,當管理的信號量只有1個時,就退化到互斥鎖。
      例如我們需要控制遠程方法的并發量,代碼如下
    semaphore.acquire(count);
    try {
    //調用遠程方法
    }
    finally {
    semaphore.release(count);
    }
      線程交換隊列
      Exchanger用于在兩個線程之間進行數據交換,線程會阻塞在Exchanger的exchange方法上,直到另外一個線程也到了同一個Exchanger的exchanger方法時,二者進行交換,然后兩個線程繼續執行自身相關代碼。
    public class TestExchanger {
    static Exchanger exchanger = new Exchanger();
    public static void main(String[] args) {
    new Thread() {
    public void run() {
    int a = 1;
    try {
    a = (int) exchanger.exchange(a);
    } catch (Exception e) {
    e.printStackTrace();
    }
    System.out.println("Thread1: "+a);
    }
    }.start();
    new Thread() {
    public void run() {
    int a = 2;
    try {
    a = (int) exchanger.exchange(a);
    } catch (Exception e) {
    e.printStackTrace();
    }
    System.out.println("Thread2: "+a);
    }
    }.start();
    }
    }
      輸出結果:
      Thread2: 1
      Thread1: 2
      并發容器
      CopyOnWrite思路是在更改容器時,把容器寫一份進行修改,保證正在讀的線程不受影響,適合應用在讀多寫少的場景,因為寫的時候重建一次容器。
      以Concurrent開頭的容器盡量保證讀不加鎖,并且修改時不影響讀,所以會達到比使用讀寫鎖更高的并發性能

    posted on 2014-05-23 10:11 順其自然EVO 閱讀(198) 評論(0)  編輯  收藏 所屬分類: 測試學習專欄

    <2014年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    導航

    統計

    常用鏈接

    留言簿(55)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 99精品国产免费久久久久久下载 | 亚洲香蕉成人AV网站在线观看| 亚洲国产成人资源在线软件| 一级黄色免费网站| 国产精品公开免费视频| 亚洲人成网站18禁止久久影院| 最新国产乱人伦偷精品免费网站 | 国产精品亚洲综合一区在线观看| 亚洲黄色免费在线观看| 亚洲国产二区三区久久| 成人精品一区二区三区不卡免费看| 免费一级大黄特色大片| 国产精品亚洲а∨无码播放不卡| 成年女人毛片免费播放视频m| 亚洲国产成人精品无码一区二区 | 16女性下面无遮挡免费| 亚洲国产品综合人成综合网站| 麻豆国产精品免费视频| 激情五月亚洲色图| 岛国片在线免费观看| 国产成人亚洲综合a∨| 亚洲免费在线观看| 久久国产精品免费视频| 亚洲狠狠ady亚洲精品大秀| 性做久久久久久久免费看| 国产精品亚洲色婷婷99久久精品| 国产一级淫片免费播放电影| 岛国精品一区免费视频在线观看| 亚洲国产精品嫩草影院在线观看| 又大又硬又爽又粗又快的视频免费| 亚洲日韩在线视频| 亚洲av成人一区二区三区在线观看| 久久久久久久久久久免费精品| 亚洲一区二区在线视频| 国产精品国产午夜免费福利看| 亚洲av无码无线在线观看| 在线a亚洲v天堂网2018| 免费A级毛片在线播放| 亚洲AV无码专区亚洲AV桃| 亚洲精品高清国产一久久| 国产一级高清免费观看|