中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中怎么測試線程的安全性

發布時間:2021-07-23 16:55:04 來源:億速云 閱讀:151 作者:Leah 欄目:編程語言

Java中怎么測試線程的安全性,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

線程安全性是Java等語言/平臺中類的一個重要標準,在Java中,我們經常在線程之間共享對象。由于缺乏線程安全性而導致的問題很難調試,因為它們是偶發的,而且幾乎不可能有目的地重現。如何測試對象以確保它們是線程安全的?

假如有一個內存書架

package com.mzc.common.thread; import java.util.Map;import java.util.concurrent.ConcurrentHashMap; /** * <p class="detail"> * 功能: 內存書架 * </p> * * @author Moore * @ClassName Books. * @Version V1.0. * @date 2019.12.10 14:00:13 */public class Books { final Map<Integer, String> map = new ConcurrentHashMap<>();  /** * <p class="detail"> * 功能: 存書,并返回書的id * </p> * * @param title : * @return int * @author Moore * @date 2019.12.10 14:00:16 */ int add(String title) { final Integer next = this.map.size() + 1; this.map.put(next, title); return next; }  /** * <p class="detail"> * 功能: 根據書的id讀取書名 * </p> * * @param id : * @return string * @author Moore * @date 2019.12.10 14:00:16 */ String title(int id) { return this.map.get(id); }}

首先,我們把一本書放進書架,書架會返回它的ID。然后,我們可以通過它的ID來讀取書名,像這樣:

Books books = new Books();String title = "Elegant Objects";int id = books.add(title);assert books.title(id).equals(title);

這個類似乎是線程安全的,因為我們使用的是線程安全的ConcurrentHashMap,而不是更原始和非線程安全的HashMap,對吧?我們先來測試一下:

public class BooksTest { @Test public void addsAndRetrieves() { Books books = new Books(); String title = "Elegant Objects"; int id = books.add(title); assert books.title(id).equals(title); }}

查看測試結果:

測試通過了,但這只是一個單線程測試。讓我們嘗試從幾個并行線程中進行相同的操作(我使用的是Hamcrest):

/** * <p class="detail"> * 功能: 多線程測試 * </p> * * @throws ExecutionException the execution exception * @throws InterruptedException the interrupted exception * @author Moore * @date 2019.12.10 14:16:34 */ @Test public void addsAndRetrieves2() throws ExecutionException, InterruptedException { Books books = new Books(); int threads = 10; ExecutorService service = Executors.newFixedThreadPool(threads); Collection<Future<Integer>> futures = new ArrayList<>(threads); for (int t = 0; t < threads; ++t) {  final String title = String.format("Book #%d", t);  futures.add(service.submit(() -> books.add(title))); } Set<Integer> ids = new HashSet<>(); for (Future<Integer> f : futures) {  ids.add(f.get()); } assertThat(ids.size(), equalTo(threads)); }

首先,我通過執行程序創建線程池。然后,我通過Submit()提交10個Callable類型的對象。他們每個都會在書架上添加一本唯一的新書。所有這些將由池中的10個線程中的某些線程以某種不可預測的順序執行。

然后,我通過Future類型的對象列表獲取其執行者的結果。最后,我計算創建的唯一圖書ID的數量。如果數字為10,則沒有沖突。我使用Set集合來確保ID列表僅包含唯一元素。

我們看一下這樣改造后的運行結果:

測試也通過了,但是,它不夠強壯。這里的問題是它并沒有真正從多個并行線程測試這些書。在兩次調用commit()之間經過的時間足夠長,可以完成books.add()的執行。這就是為什么實際上只有一個線程可以同時運行的原因。

我們可以通過修改一些代碼再來檢查它:

@Test public void addsAndRetrieves3() {  Books books = new Books();  int threads = 10;  ExecutorService service = Executors.newFixedThreadPool(threads);  AtomicBoolean running = new AtomicBoolean();  AtomicInteger overlaps = new AtomicInteger();  Collection<Future<Integer>> futures = new ArrayList<>(threads);  for (int t = 0; t < threads; ++t) {   final String title = String.format("Book #%d", t);   futures.add(     service.submit(       () -> {        if (running.get()) {         overlaps.incrementAndGet();        }        running.set(true);        int id = books.add(title);        running.set(false);        return id;       }     )   );  }  assertThat(overlaps.get(), greaterThan(0)); }

看一下測試結果:

執行錯誤,說明插入的書和返回的id數量是不沖突的。

通過上面的代碼,我試圖了解線程之間的重疊頻率以及并行執行的頻率。但是基本上概率為0,所以這個測試還沒有真正測到我想測的,還不是我們想要的,它只是把十本書一本一本地加到書架上。

再來:

可以看到,如果我把線程數增加到1000,它們會開始重疊或者并行運行。

但是我希望即使線程數只有10個的時候,也會出現重疊并行的情況。怎么辦呢?為了解決這個問題,我使用CountDownLatch:

@Test  public void addsAndRetrieves4() throws ExecutionException, InterruptedException {    Books books = new Books();    int threads = 10;    ExecutorService service = Executors.newFixedThreadPool(threads);    CountDownLatch latch = new CountDownLatch(1);    AtomicBoolean running = new AtomicBoolean();    AtomicInteger overlaps = new AtomicInteger();    Collection<Future<Integer>> futures = new ArrayList<>(threads);    for (int t = 0; t < threads; ++t) {      final String title = String.format("Book #%d", t);      futures.add(          service.submit(              () -> {                latch.await();                if (running.get()) {                  overlaps.incrementAndGet();                }                running.set(true);                int id = books.add(title);                running.set(false);                return id;              }          )      );    }    latch.countDown();    Set<Integer> ids = new HashSet<>();    for (Future<Integer> f : futures) {      ids.add(f.get());    }    assertThat(overlaps.get(), greaterThan(0));  }

現在,每個線程在接觸書本之前都要等待鎖權限。當我們通過Submit()提交所有內容時,它們將保留并等待。然后,我們用countDown()釋放鎖,它們才同時開始運行。

查看運行結果:

通過運行結果可以知道,現在線程數還是為10,但是線程的重疊數是大于0的,所以assertTrue執行通過,ids也不等于10了,也就是沒有像以前那樣得到10個圖書ID。顯然,Books類不是線程安全的!

在修復優化該類之前,教大家一個簡化測試的方法,使用來自Cactoos的RunInThreads,它與我們上面所做的完全一樣,但代碼是這樣的:

@Test  public void addsAndRetrieves5() {    Books books = new Books();    MatcherAssert.assertThat(        t -> {          String title = String.format(              "Book #%d", t.getAndIncrement()          );          int id = books.add(title);          return books.title(id).equals(title);        },        new RunsInThreads<>(new AtomicInteger(), 10)    );  }

assertThat()的第一個參數是Func(一個函數接口)的實例,接受AtomicInteger(RunsThreads的第一個參數)并返回布爾值。此函數將在10個并行線程上執行,使用與上述相同的基于鎖的方法。

這個RunInThreads看起來非常緊湊,用起來也很方便,推薦給大家,可以用起來的。只需要在你的項目中添加一個依賴:

<dependency>      <groupId>org.llorllale</groupId>      <artifactId>cactoos-matchers</artifactId>      <version>0.18</version>    </dependency>

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

稷山县| 南乐县| 伊宁县| 延安市| 辽中县| 于都县| 宾阳县| 临沧市| 炉霍县| 汝州市| 泌阳县| 泉州市| 黑山县| 盈江县| 井研县| 正阳县| 视频| 逊克县| 汶川县| 裕民县| 兰坪| 银川市| 长治县| 蓝山县| 荆州市| 达州市| 青岛市| 武鸣县| 六枝特区| 博白县| 渝北区| 木兰县| 沈阳市| 雷山县| 杨浦区| 邢台市| 丁青县| 吴江市| 吉首市| 萨迦县| 昌黎县|