在 Java 中使用异步线程编辑文件

Posted

技术标签:

【中文标题】在 Java 中使用异步线程编辑文件【英文标题】:Editing a file using async threads in Java 【发布时间】:2021-11-11 07:30:24 【问题描述】:

我是一名小型 Java 开发人员,目前正在开发一个我用 Java 制作的不和谐机器人。我的机器人的功能之一是只要有人发送消息(以及其他条件,但这与我遇到的问题无关),就可以简单地拥有一个调平系统。

每当有人发送消息时,都会触发一个事件并创建一个线程来计算用户应该获得多少 exp。最终调用了编辑存储文件的函数。

在稀疏调用时效果很好。但是如果两个线程尝试一次写入文件,文件通常会被删除或截断。这两种情况中的任何一种都是不良行为

然后我尝试制作一个工作超过 24 小时但仍然失败一次的排队系统,因此它在某种程度上更稳定。我只知道线程如何工作的基础知识,所以我可能跳过了导致问题的重要事情

函数是这样的

    Thread editingThread = null;
    public boolean editThreadStarted = false;
    HashMap<String, String> queue = new HashMap<>();


    public final boolean editParameter(String key, String value) 
        queue.put(key, value);

        if(!editThreadStarted) 
            editingThread = new Thread(new Runnable() 

                @Override
                public void run() 

                    while(queue.keySet().size() > 0) 
                        String key = (String) queue.keySet().toArray()[0];
                        String value = queue.get(key);
                        
                        File inputFile = getFile();
                        File tempFile = new File(getFile().getName() + ".temp");
                        try 
                            tempFile.createNewFile();
                         catch (IOException e) 
                            DemiConsole.error("Failed to create temp file");
                            handleTrace(e);
                            continue;
                        
                        //System.out.println("tempFile.isFile = " + tempFile.isFile());
                        try (BufferedReader reader = new BufferedReader(new FileReader(inputFile)); BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile)))
                            String currentLine;

                            while((currentLine = reader.readLine()) != null) 
                                String trimmedLine = currentLine.trim();
                                if(trimmedLine.startsWith(key)) 
                                    writer.write(key + ":" + value + System.getProperty("line.separator"));
                                    continue;
                                
                                writer.write(currentLine + System.getProperty("line.separator"));
                            
                            writer.close(); 
                            reader.close(); 
                            inputFile.delete();
                            tempFile.renameTo(inputFile);
                         catch (IOException e) 
                            DemiConsole.error("Caught an IO exception while attempting to edit parameter ("+key+") in file ("+getFile().getName()+"), returning false");
                            handleTrace(e);
                            continue;
                        
                        queue.remove(key);
                    
                    editThreadStarted = false;
                

            );
            editThreadStarted = true;
            editingThread.start();
        
        return true;
    

getFile() 返回函数要写入的文件

文件格式是

memberid1:expamount

memberid2:expamount

memberid3:expamount

memberid4:expamount

编辑工作的方式是创建一个临时文件,我将逐行写入原始文件的所有数据,检查 memberid 是否与我要编辑的内容匹配,如果匹配,则不要写入原始文件的行,我将用新的 expamount 编写新的编辑行,然后继续其余行。完成后,将删除原始文件并将临时文件重命名为原始文件,并替换它。

这个函数总是被异步调用,所以让整个事情同步不是一个选项。

提前致谢

编辑(1):

有人建议我使用信号量,在深入研究之后(我以前从未听说过信号量),这似乎是一个非常好的选择,并且无需排队,只需在开始时获取并发布最后,什么都不需要了!

【问题讨论】:

你考虑过同步吗?信号量?有任何顺序化机制吗? 如何使用您提到的队列并让一个线程实际写入文件?此外,根据用户的数量,您可能需要考虑将整个文件保存在内存中(以某种结构)并定期编写新版本(在更新或一堆更新之后)。或者,您可能想考虑某种分片和索引,以便更快地找到需要更新的记录,从而减轻编写器线程的负担。 对于队列系统,其实是我刚刚发布的函数,至于将整个东西存储在hashMap中似乎是最简单的解决方案,我们大约有32k个成员,我不确定它是否会真的很有效率,我会尝试以这种方式实现它,如果遇到任何其他问题,我会回来。谢谢! 【参考方案1】:

我最终按照user207421 的建议使用了semaphores,它似乎工作得很好

我只是在每行写入之间设置延迟,以人为地使任务更长,并使多个线程同时尝试写入变得更容易,它们都在等待轮到自己!

谢谢

【讨论】:

以上是关于在 Java 中使用异步线程编辑文件的主要内容,如果未能解决你的问题,请参考以下文章

JAVA语言中请写出线程从阻塞状态恢复到就绪状态的三种途径

sqlite还是文件存储?

JAVA语言异步非阻塞设计模式(原理篇)

luckysheet关闭异步获取sheet数据

Win32 函数可以异步运行在同一个线程上吗?

Java-多线程之初识线程