欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中常見的并發(fā)控制手段淺析

 更新時間:2021年08月13日 09:53:25   作者:一灰灰  
所謂并發(fā)控制就是幫助我們程序員更容易的讓線程之間合作,讓線程之間相互配合來滿足業(yè)務邏輯,這篇文章主要給大家介紹了關于Java中常見的并發(fā)控制手段的相關資料,需要的朋友可以參考下

前言

單實例的并發(fā)控制,主要是針對JVM內(nèi),我們常規(guī)的手段即可滿足需求,常見的手段大概有下面這些

  • 同步代碼塊
  • CAS自旋
  • 阻塞隊列,令牌桶等

1.1 同步代碼塊

通過同步代碼塊,來確保同一時刻只會有一個線程執(zhí)行對應的業(yè)務邏輯,常見的使用姿勢如下

public synchronized doProcess() {
    // 同步代碼塊,只會有一個線程執(zhí)行
}

一般推薦使用最小區(qū)間使用原則,盡量不要直接在方法上加synchronized,比如經(jīng)典的雙重判定單例模式

public class Single {
  private static volatile Single instance;
  private Single() {}
  public static Single getInstance() {
      if (instance == null) {
          synchronized(Single.class) {
              if (instance == null) instance = new Single();
          }
      }
      return instance;
  }
}

1.2 CAS自旋方式

比如AtomicXXX原子類中的很多實現(xiàn),就是借助unsafe的CAS來實現(xiàn)的,如下

public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}


// unsafe 實現(xiàn)
// cas + 自選,不斷的嘗試更新設置,直到成功為止
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

1.3 鎖

jdk本身提供了不少的鎖,為了實現(xiàn)單實例的并發(fā)控制,我們需要選擇寫鎖;如果支持多讀,單實例寫,則可以考慮讀寫鎖;一般使用姿勢也比較簡單

private void doSome(ReentrantReadWriteLock.WriteLock writeLock) {
    try {
        writeLock.lock();
        System.out.println("持有鎖成功 " + Thread.currentThread().getName());
        Thread.sleep(1000);
        System.out.println("執(zhí)行完畢! " + Thread.currentThread().getName());
        writeLock.unlock();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Test
public void lock() throws InterruptedException {
    ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

    new Thread(()->doSome(reentrantReadWriteLock.writeLock())).start();
    new Thread(()->doSome(reentrantReadWriteLock.writeLock())).start();
    new Thread(()->doSome(reentrantReadWriteLock.writeLock())).start();

    Thread.sleep(20000);
}

1.4 阻塞隊列

借助同步阻塞隊列,也可以實現(xiàn)并發(fā)控制的效果,比如隊列中初始化n個元素,每次消費從隊列中獲取一個元素,如果拿不到則阻塞;執(zhí)行完畢之后,重新塞入一個元素,這樣就可以實現(xiàn)一個簡單版的并發(fā)控制

demo版演示,下面指定隊列長度為2,表示最大并發(fā)數(shù)控制為2;設置為1時,可以實現(xiàn)單線程的訪問控制

AtomicInteger cnt = new AtomicInteger();

private void consumer(LinkedBlockingQueue<Integer> queue) {
    try {
        // 同步阻塞拿去數(shù)據(jù)
        int val = queue.take();
        Thread.sleep(2000);
        System.out.println("成功拿到: " + val + " Thread: " + Thread.currentThread());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        // 添加數(shù)據(jù)
        System.out.println("結束 " + Thread.currentThread());
        queue.offer(cnt.getAndAdd(1));
    }
}

@Test
public void blockQueue() throws InterruptedException {
    LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(2);
    queue.add(cnt.getAndAdd(1));
    queue.add(cnt.getAndAdd(1));


    new Thread(() -> consumer(queue)).start();
    new Thread(() -> consumer(queue)).start();
    new Thread(() -> consumer(queue)).start();
    new Thread(() -> consumer(queue)).start();

    Thread.sleep(10000);
}

1.5 信號量Semaphore

上面隊列的實現(xiàn)方式,可以使用信號量Semaphore來完成,通過設置信號量,來控制并發(fā)數(shù)

private void semConsumer(Semaphore semaphore) {
    try {
        //同步阻塞,嘗試獲取信號
        semaphore.acquire(1);
        System.out.println("成功拿到信號,執(zhí)行: " + Thread.currentThread());
        Thread.sleep(2000);
        System.out.println("執(zhí)行完畢,釋放信號: " + Thread.currentThread());
        semaphore.release(1);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Test
public void semaphore() throws InterruptedException {
    Semaphore semaphore = new Semaphore(2);

    new Thread(() -> semConsumer(semaphore)).start();
    new Thread(() -> semConsumer(semaphore)).start();
    new Thread(() -> semConsumer(semaphore)).start();
    new Thread(() -> semConsumer(semaphore)).start();
    new Thread(() -> semConsumer(semaphore)).start();

    Thread.sleep(20_000);
}

1.6 計數(shù)器CountDownLatch

計數(shù),應用場景更偏向于多線程的協(xié)同,比如多個線程執(zhí)行完畢之后,再處理某些事情;不同于上面的并發(fā)數(shù)的控制,它和柵欄一樣,更多的是行為結果的統(tǒng)一

這種場景下的使用姿勢一般如下

重點:countDownLatch 計數(shù)為0時放行

@Test
public void countDown() throws InterruptedException {
    CountDownLatch countDownLatch = new CountDownLatch(2);

    new Thread(() -> {
        try {
            System.out.println("do something in " + Thread.currentThread());
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            countDownLatch.countDown();
        }
    }).start();

    new Thread(() -> {
        try {
            System.out.println("do something in t2: " + Thread.currentThread());
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            countDownLatch.countDown();
        }
    }).start();

    countDownLatch.await();
    System.out.printf("結束");
}

1.7 柵欄 CyclicBarrier

CyclicBarrier的作用與上面的CountDownLatch相似,區(qū)別在于正向計數(shù)+1, 只有達到條件才放行; 且支持通過調(diào)用reset()重置計數(shù),而CountDownLatch則不行

一個簡單的demo

private void cyclicBarrierLogic(CyclicBarrier barrier, long sleep) {
    // 等待達到條件才放行
    try {
        System.out.println("準備執(zhí)行: " + Thread.currentThread() + " at: " + LocalDateTime.now());
        Thread.sleep(sleep);
        int index = barrier.await();
        System.out.println("開始執(zhí)行: " + index + " thread: " + Thread.currentThread() + " at: " + LocalDateTime.now());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Test
public void testCyclicBarrier() throws InterruptedException {
    // 到達兩個工作線程才能繼續(xù)往后面執(zhí)行
    CyclicBarrier barrier = new CyclicBarrier(2);
    // 三秒之后,下面兩個線程的才會輸出 開始執(zhí)行
    new Thread(() -> cyclicBarrierLogic(barrier, 1000)).start();
    new Thread(() -> cyclicBarrierLogic(barrier, 3000)).start();

    Thread.sleep(4000);
    // 重置,可以再次使用
    barrier.reset();
    new Thread(() -> cyclicBarrierLogic(barrier, 1)).start();
    new Thread(() -> cyclicBarrierLogic(barrier, 1)).start();
    Thread.sleep(10000);
}

1.8 guava令牌桶

guava封裝了非常簡單的并發(fā)控制工具類RateLimiter,作為單機的并發(fā)控制首選

一個控制qps為2的簡單demo如下:

private void guavaProcess(RateLimiter rateLimiter) {
    try {
        // 同步阻塞方式獲取
        System.out.println("準備執(zhí)行: " + Thread.currentThread() + " > " + LocalDateTime.now());
        rateLimiter.acquire();
        System.out.println("執(zhí)行中: " + Thread.currentThread() + " > " + LocalDateTime.now());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Test
public void testGuavaRate() throws InterruptedException {
    // 1s 中放行兩個請求
    RateLimiter rateLimiter = RateLimiter.create(2.0d);
    new Thread(() -> guavaProcess(rateLimiter)).start();
    new Thread(() -> guavaProcess(rateLimiter)).start();
    new Thread(() -> guavaProcess(rateLimiter)).start();
    new Thread(() -> guavaProcess(rateLimiter)).start();
    new Thread(() -> guavaProcess(rateLimiter)).start();
    new Thread(() -> guavaProcess(rateLimiter)).start();
    new Thread(() -> guavaProcess(rateLimiter)).start();

    Thread.sleep(20_000);
}

輸出:

準備執(zhí)行: Thread[Thread-2,5,main] > 2021-04-13T10:18:05.263
準備執(zhí)行: Thread[Thread-1,5,main] > 2021-04-13T10:18:05.263
準備執(zhí)行: Thread[Thread-5,5,main] > 2021-04-13T10:18:05.264
準備執(zhí)行: Thread[Thread-7,5,main] > 2021-04-13T10:18:05.264
準備執(zhí)行: Thread[Thread-3,5,main] > 2021-04-13T10:18:05.263
準備執(zhí)行: Thread[Thread-4,5,main] > 2021-04-13T10:18:05.264
準備執(zhí)行: Thread[Thread-6,5,main] > 2021-04-13T10:18:05.263
執(zhí)行中: Thread[Thread-2,5,main] > 2021-04-13T10:18:05.267
執(zhí)行中: Thread[Thread-6,5,main] > 2021-04-13T10:18:05.722
執(zhí)行中: Thread[Thread-4,5,main] > 2021-04-13T10:18:06.225
執(zhí)行中: Thread[Thread-3,5,main] > 2021-04-13T10:18:06.721
執(zhí)行中: Thread[Thread-7,5,main] > 2021-04-13T10:18:07.221
執(zhí)行中: Thread[Thread-5,5,main] > 2021-04-13T10:18:07.720
執(zhí)行中: Thread[Thread-1,5,main] > 2021-04-13T10:18:08.219

1.9 滑動窗口TimeWindow

沒有找到通用的滑動窗口jar包,一般來講滑動窗口更適用于平滑的限流,解決瞬時高峰問題

一個供參考的實現(xiàn)方式:

固定大小隊列,隊列中每個數(shù)據(jù)代表一個時間段的計數(shù),

訪問 -》 隊列頭拿數(shù)據(jù)(注意不出隊)-》判斷是否跨時間段 -》 同一時間段,計數(shù)+1 -》跨時間段,新增數(shù)據(jù)入隊,若

扔不進去,表示時間窗滿,隊尾數(shù)據(jù)出隊

問題:當流量稀疏時,導致不會自動釋放過期的數(shù)據(jù)

解決方案:根據(jù)時間段設置定時任務,模擬訪問操作,只是將計數(shù)改為 + 0

1.10 小結

本文給出了幾種單機版的并發(fā)控制的技術手段,主要目的是介紹了一些可選的方案,技術細節(jié)待后續(xù)補全完善,當然如果有其他的建議,歡迎評論交流

到此這篇關于Java中常見的并發(fā)控制手段的文章就介紹到這了,更多相關Java并發(fā)控制手段內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 基于SpringBoot bootstrap.yml配置未生效的解決

    基于SpringBoot bootstrap.yml配置未生效的解決

    這篇文章主要介紹了基于SpringBoot bootstrap.yml配置未生效的解決方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • Java Web三層架構的配置詳解

    Java Web三層架構的配置詳解

    這篇文章主要介紹了Java Web三層架構的配置方法,需要的朋友可以參考下
    2014-10-10
  • 基于java讀取并引用自定義配置文件

    基于java讀取并引用自定義配置文件

    這篇文章主要介紹了基于java讀取并引用自定義配置文件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06
  • java兩個數(shù)組合并為一個數(shù)組的幾種方法

    java兩個數(shù)組合并為一個數(shù)組的幾種方法

    這篇文章主要給大家介紹了關于java兩個數(shù)組合并為一個數(shù)組的幾種方法,最近在寫代碼時遇到了需要合并兩個數(shù)組的需求,文中將每種方法都介紹的非常詳細,需要的朋友可以參考下
    2023-07-07
  • RocketMq深入分析講解兩種削峰方式

    RocketMq深入分析講解兩種削峰方式

    當上游調(diào)用下游服務速率高于下游服務接口QPS時,那么如果不對調(diào)用速率進行控制,那么會發(fā)生很多失敗請求,通過消息隊列的削峰方法有兩種,這篇文章主要介紹了RocketMq深入分析講解兩種削峰方式
    2023-01-01
  • Springboot實現(xiàn)多線程及線程池監(jiān)控

    Springboot實現(xiàn)多線程及線程池監(jiān)控

    線程池的監(jiān)控很重要,本文就來介紹一下Springboot實現(xiàn)多線程及線程池監(jiān)控,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學習學習吧
    2024-01-01
  • springboot如何使用sm2加密傳輸

    springboot如何使用sm2加密傳輸

    這篇文章主要介紹了springboot如何使用sm2加密傳輸問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • Java操作XML工具類XmlUtil詳解

    Java操作XML工具類XmlUtil詳解

    這篇文章主要為大家詳細介紹了Java操作XML工具類XmlUtil的使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • Java基礎之簡單介紹一下Maven

    Java基礎之簡單介紹一下Maven

    今天給大家復習一下Java基礎知識,簡單介紹Maven,文中有非常詳細的解釋,對Java初學者很有幫助喲,需要的朋友可以參考下
    2021-05-05
  • Java Base64算法實際應用之郵件發(fā)送實例分析

    Java Base64算法實際應用之郵件發(fā)送實例分析

    這篇文章主要介紹了Java Base64算法實際應用之郵件發(fā)送,結合實例形式分析了java字符編碼與郵件發(fā)送相關操作技巧,需要的朋友可以參考下
    2019-09-09

最新評論