簡體   English   中英

Java代碼競爭條件是多線程的嗎?

[英]Java code race condition multi-threaded?

我有一些代碼,我想知道在多線程環境中是否會丟失數據...

這是示例代碼:

public class TestingJavaThreading {
    private final Map<String, Set<String>> data = Maps.newConcurrentMap();
    private final HttpClient client;
    private final AsyncDataProvider provider;
    private final String baseUrl;

    // This method is called first...
    public void init(String code) {
        // We initialise the set to ensure it doesn't throw a null pointer exception or something weird...
        data.put(code, Sets.newConcurrentHashSet());

        // We tell the provider we're interested in data...
        provider.subscribeToDataFrom(code);

        // This HTTP call may take long time, and we can't afford losing data, that's why we subscribed beforehand in the previous line...
        List<String> elements = client.request(baseUrl + code);

        // We add all of the new elements, meanwhile some elements may have been added by "onMessageFromProvider"
        data.get(code).addAll(elements);

        data.get(code)
            .stream()
            .map( /* some transformations here, whatever... */)
            .forEach(e -> System.out.println(e));

        // Now we've printed the merged data from "onMessageFromProvider" + the HTTP call
        // We remove the element from the map, so now we only receive data from "onMessageFromProvider"
        data.remove(code); 
    }

    public void onMessageFromProvider(String code, String element) {
        final Set<String> newSet = data.computeIfPresent(code, (k, v) -> {
          v.add(element);
          return v;
        });

        if (newSet == null) {
            // Do something else...
        }
    }
}

基本上,被調用的初始方法是init 步驟如下:

  1. 初始化CHM以確保它包含數據
  2. 我們有一個提供程序,可為我們實時提供有關該元素的信息,但它不提供有關它的過去數據。 當數據來自提供程序時,它將調用方法“ onMessageFromProvider”
  3. 為了獲得該項目的先前數據,我們需要執行一個單獨的HTTP調用,然后將來自“ onMessageFromProvider”的數據與HTTP調用的結果合並。 完成之后,我們可以完全依靠“ onMessageFromProvider”所做的一切
  4. 從HTTP調用獲得結果后,我們將其與來自“ onMessageFromProvider”的數據合並,同時,我們應用轉換,並打印結果合並集
  5. 現在我們刪除了映射鍵,因此我們可以完全依靠“ onMessageFromProvider”所做的任何事情

在運行步驟(3)時,是否可能導致數據丟失? 我該如何解決? 我應該在哪里放置更多代碼,以便盡可能少地依賴於synchronished

因此,繼續進行下去,我的目標是永不丟失數據,並且我想確保我的算法能夠100%保證。

很抱歉,您的長期評價是有道理的。

UPDATE

基於輸入,我將使用一個真實的示例更新代碼,當前看起來像這樣:

public class Main {

  public static void main(String[] args) throws InterruptedException {
    new Main().init("X");
  }

  public void init(String code) throws InterruptedException {
    subscribeToDataFrom(code);

    CompletableFuture
        .supplyAsync(getDataFromHttpRequest());
  }

  private Supplier<Set<String>> getDataFromHttpRequest() {
    return () -> {
      Set<String> resultsToReturn = Sets.newHashSet();
      try {
        resultsToReturn.add("B");
        resultsToReturn.add("C");
        resultsToReturn.add("D");
        resultsToReturn.add("E");
        resultsToReturn.add("F");
        Thread.sleep(1000); // Simulate it is a slow request...
      } catch (Exception ex) {}

      return resultsToReturn;
    };
  }

  private void subscribeToDataFrom(String code) {
    Runnable r = () -> {
      while (true) {
        onMessageFromProvider(code, UUID.randomUUID().toString());
      }
    };

    new Thread(r).start();
    new Thread(r).start();
    new Thread(r).start();
    new Thread(r).start();
    new Thread(r).start();
  }

  public void onMessageFromProvider(String code, String element) {
    // Here how do I create the completable future for usage in the previous CompletableFuture????

    final Set<String> newSet = data.computeIfPresent(code, (k, v) -> {
      v.add(element);
      return v;
    });

    if (newSet == null) {
      System.out.println("Ok, now I can do something different with: " + element);
    }
  }
}

CompletableFuture類,具有組合和執行差異任務的方法

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html

  • 為非異步方法的相關完成提供的動作可以由完成當前CompletableFuture的線程執行,也可以由完成方法的任何其他調用者執行。
  • 所有沒有顯式Executor參數的異步方法都是使用ForkJoinPool.commonPool()執行的(除非它不支持並行度至少為2,在這種情況下,將創建一個新的Thread來運行每個任務)。 為了簡化監視,調試和跟蹤,所有生成的異步任務都是標記接口CompletableFuture.AsynchronousCompletionTask的實例。
  • 所有CompletionStage方法都是獨立於其他公共方法實現的,因此一個方法的行為不受子類中其他方法的覆蓋影響。

例如 :

  CompletableFuture<String> httpTask = CompletableFuture.supplyAsync(
                                 () -> new HttpTask(httpClient));

  Set<String> result = httpTask.thenApply(elements -> //onMessageProvider method       
                   // maybe you can create a Callable class with the logic)

          .thenApply(mergedElements -> //remove code).get();
          //or try another method


  class HttpTask extends Callable<List<String>>{

    private HttpClient client;

    public HttpTask(HttpClient client){
        this.client = client;
    }

    @Override
    public List<String> call() throws Exception {
      return client.httpCall(...);
    }
   }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM