使用 Executors 时出现空地图

huangapple go评论76阅读模式
英文:

Empty Map when using Executors

问题

我正试图对于给定时间范围内的一个 List 中,与参数中的 type 相同的记录,每分钟进行计数。使用以下方法:

public Map<String, Object> getCountPerMinuteForType(final String type,
                                 final long startTimestamp,
                                 final long endTimestamp) {
    final Map<String, Object> countsPerMinForType = new HashMap<>();
    Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
            int counter = 0;
            List<Data> dataList = storage.retrieveData();
            for(Data data: dataList){
                if (data.getType().equals(type) &&
                    data.getUnixTimestamp() >= startTimestamp &&
                    data.getUnixTimestamp() <= endTimestamp){
                    counter++;
                }
            }
            countsPerMinForType.put(type, counter);
        }, 0, 1, TimeUnit.MINUTES);
    return countsPerMinForType;
}

问题是,这个方法返回一个空的 Map

当我在 Executors 内部打印 Map 的内容时,我可以看到它有数据。

英文:

I'm trying to count for each minute the records with the same type as in the parameter in a List, in a given time range. Using the following Method:

public Map&lt;String, Object&gt; getCountPerMinuteForType(final String type,
                                 final long startTimestamp,
                                 final long endTimestamp) {
    final Map&lt;String, Object&gt; countsPerMinForType = new HashMap&lt;&gt;();
    Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -&gt; {
            int counter = 0;
            List&lt;Data&gt; dataList = storage.retrieveData();
            for(Data data: dataList){
                if (data.getType().equals(type) &amp;&amp;
                    data.getUnixTimestamp() &gt;= startTimestamp &amp;&amp;
                    data.getUnixTimestamp() &lt;= endTimestamp){
                    counter++;
                }
            }
            countsPerMinForType.put(type, counter);
        }, 0, 1, TimeUnit.MINUTES);
    return countsPerMinForType;
}

The problem is, this method returns an empty Map.

When I print the contents of the Map inside the Executors, I can see that it has data.

答案1

得分: 0

因为另一个线程在 Map 中执行了放置操作,所以出现了这个问题。主线程开始执行该线程,然后返回到调用它的地方。要解决这个问题,您可能需要创建一个监听器接口,每当第二个线程执行任务时就会被调用。

下面是您可以根据自己的需求使用和修改的代码示例:

class Test implements Listener {
    private Listener listener;

    public Test() {
        listener = this; // 将这个类设置为您的监听器
    }

    // 使您的函数不返回任何内容
    public void getCountPerMinuteForType(final String type, final long startTimestamp, 
    final long endTimestamp) {

    final Map<String, Object> countsPerMinForType = new HashMap<>();
    ScheduledExecutorService service = 
    Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
        int counter = 0;
        List<Data> dataList = storage.retrieveData();
        for (Data data : dataList) {
            if (data.getType().equals(type) &&
                    data.getUnixTimestamp() >= startTimestamp &&
                    data.getUnixTimestamp() <= endTimestamp) {
                counter++;
            }
        }
        listener.onNewData(type, counter);
        countsPerMinForType.put(type, counter);
        }, 0, 1, TimeUnit.MINUTES);
        //return countsPerMinForType;
        // 如果服务被终止,调用监听器并在那里执行您的操作
        if (service.isTerminated()) {
            listener.dataFilled(countsPerMinForType);
        }

    }

    @Override
    public void onNewData(String str, Object obj) {
        // 在这里执行您的任务
    }
    @Override
    public void dataFilled(Map<String, Object> data) {
        // 在这里执行您的任务
    }
}

interface Listener {
    void dataFilled(Map<String, Object> data);
    void onNewData(Map<String, Object> data);
}

请注意,上面的代码示例可能需要根据您的实际需求进行一些调整和修改。

英文:

It happens because an another thread is performing put operation in the Map. The main thread starts execution of the thread and then returns back to the place where it was called. To solve this issue, you may need to create a listener interface which gets called whenever the second thread performs the task.

Here, below is the code sample which you can use and modify according to your needs.

class Test implements Listener {
private Listener listener;
public Test() {
listener = this; //Set this class as your listener
}
//Make your function return nothing
public void getCountPerMinuteForType(final String type, final long startTimestamp, 
final long endTimestamp) {
final Map&lt;String, Object&gt; countsPerMinForType = new HashMap&lt;&gt;();
ScheduledExecutorService service = 
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -&gt; {
int counter = 0;
List&lt;Data&gt; dataList = storage.retrieveData();
for (Data data : dataList) {
if (data.getType().equals(type) &amp;&amp;
data.getUnixTimestamp() &gt;= startTimestamp &amp;&amp;
data.getUnixTimestamp() &lt;= endTimestamp) {
counter++;
}
}
listener.onNewData(type, counter);
countsPerMinForType.put(type, counter);
}, 0, 1, TimeUnit.MINUTES);
//return countsPerMinForType;
//If service is terminated call the listener and perform your operation there
if (service.isTerminated()) {
listener.dataFilled(countsPerMinForType);
}
}
@Override
public void onNewData(String str, Object obj) {
//Perform your task here
}
@Override
public void dataFilled(Map&lt;String, Object&gt; data) {
//Perform your task here
}
}
interface Listener {
void dataFilled(Map&lt;String, Object&gt; data);
void onNewData(Map&lt;String, Object&gt; data);
}

答案2

得分: 0

你遇到的问题是你期望你所启动的线程完成工作,并且将结果填充到countsPerMinForType中然后返回。但实际情况并非如此...

发生的情况是:

  1. 你从主线程/当前执行线程调用该方法。
  2. 创建了一个Map
  3. 启动一个新线程来执行一些工作。
  4. 几乎立即,该方法就返回了,此时映射仍然为空。
    ...
  5. 在方法完成之后,新线程执行的工作会被执行... 随后调用方法永远无法看到结果。

你可以通过一个测试来确认这一点,该测试返回getCountPerMinuteForType开始和结束的timestamp,以及Thread开始和结束的另一个timestamp。开始时间将是有序的,而结束时间将不会按顺序。

此外,考虑在多线程应用程序中使用ConcurrentHashMap可能会是一个好主意。

英文:

The issue you have is that you're expecting the thread you're spinning off has completed the work and the results populated in the countsPerMinForType are returned. This is not what is happening...

What is happening is:

  1. you call the method from the main/current thread of execution
  2. the Map is created
  3. a new thread is spun off to do some work
  4. almost immediately, the method returns and the map is still empty.
    ...
  5. after the method has completed, the work being performed by the spun off thread is then carried out... and subsequently the calling method never sees the result.

You can confirm this is the case with a test that returns a timestamp for when the getCountPerMinuteForType starts and ends, and another timestamp for when the Thread starts and ends. The start times will be in order, the end times will not be in order.

Also, you may want to consider using a ConcurrentHashMap for a multi-threaded application.

huangapple
  • 本文由 发表于 2020年10月2日 21:16:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/64172226.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定