我的问题是这样的:读取一个巨大的(数百万行)文件如何使线程保持 Activity 状态,即使它完成了文件操作
问题是我有一个从 javafx 应用程序线程启动的线程,然后(新线程)对文本文件执行一些读/写操作,当面对要解析的巨大文件时不会自然退出,具体来说,有 1700 万行。
我假设这是由于线程保留了我缺少的某些资源,但是,由于我使用的是 try-with-resource 模型,我不确定这实际上是如何可能的。
这是引发线程的 javafx Controller 类(使用 fxml):
MainController.java:
/**
* This method handles what happens when the calculate button is clicked.
* The main thing this does is disable/enable a few Nodes, as well as sparks
* off the background thread.
*
* @param event
*/
@FXML
private void convert_button_action(ActionEvent event) {
closing_label.setVisible(true);
convert_button.setDisable(true);
input_text = input_NCLocation_field.getText();
output_text = output_Location_Field.getText();
indicator_node.setVisible(true);
if (!toggleSwitch.isSelected()) {
(new Thread(new FileWriter(input_text, output_text, indicator_node))).start();
} else {
DateWriter temp = new DateWriter(input_text, output_text, indicator_node, yr_mn_dy.isSelected());
(new Thread(temp)).start();
}
}
其中没有什么太花哨的东西,只是一些使事物可见/不可见以及基于用户输入启动适当线程的内容。接下来是整个 Thread 类,因为它不是太大。它所做的只是将看起来像这样的行: 年月日 转换为年、月、日,或者如果用户单击了要求的复选框,则将年月和日列分隔成单独的文件。只是针对用例的方便工具。
请注意 run()
方法末尾的 println 语句。我每次都会看到这个 println,但是发生之后什么也没有发生。程序没有退出,线程没有停止,什么也没有。
package File_Conversion;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import javafx.application.Platform;
import javafx.scene.control.ProgressIndicator;
/**
* This class is the background 'worker' thread that does all of the heavy duty
* file IO for splitting up the NC file. It periodically sends reports back to
* the main application thread to update the progress indicator.
*
* @author William
*/
public class DateWriter implements Runnable {
private final ProgressIndicator myIndicator;
private static File ncFile;
private final String outputLocationFile;
private float zmax, zmin, xmax, xmin, ymax, ymin;
private ArrayList<Float> xList, yList, zList;
private final DecimalFormat numberFormat = new DecimalFormat("#.000000");
private final DecimalFormat numberFormatMinMax = new DecimalFormat("#.00000");
private final boolean yr_mon_day;
/**
* This is the main constructor, it needs a valid NC file to continue.
*
* @param inputNCFile
* @param outputLocation
* @param myIndicator
* @param yr_mon_day
*/
public DateWriter(String inputNCFile, String outputLocation, ProgressIndicator myIndicator, boolean yr_mon_day) {
this.yr_mon_day = yr_mon_day;
this.myIndicator = myIndicator;
ncFile = new File(inputNCFile);
outputLocationFile = outputLocation;
}
/**
* The primary run() method, starts the thread.
*/
@Override
public void run() {
convertDate();
Platform.runLater(new Runnable() {
@Override
public void run() {
File_Conversion.stage_returner().close();
}
});
System.out.println("I'm at the end of the run...??");
}
public boolean convertDate() {
BufferedReader br = null;
java.io.FileWriter yearWriter = null, MonthWriter = null, DayWriter = null
,fWriter = null;
BufferedWriter yearBuf = null, monthBuf = null, dayBuf = null, writer = null;
try {
br = new BufferedReader(new FileReader(ncFile));
if (yr_mon_day) {
yearWriter = new java.io.FileWriter(outputLocationFile + "\\" + ncFile.getName().substring(0, ncFile.getName().lastIndexOf(".")) + "_modified_year.csv", false);
yearBuf = new BufferedWriter(yearWriter);
MonthWriter = new java.io.FileWriter(outputLocationFile + "\\" + ncFile.getName().substring(0, ncFile.getName().lastIndexOf(".")) + "_modified_month.csv", false);
monthBuf = new BufferedWriter(MonthWriter);
DayWriter = new java.io.FileWriter(outputLocationFile + "\\" + ncFile.getName().substring(0, ncFile.getName().lastIndexOf(".")) + "_modified_day.csv", false);
dayBuf = new BufferedWriter(DayWriter);
String input;
String temp;
String temp2;
String temp3;
while ((input = br.readLine()) != null) {
temp = input.substring(0, 4);
temp2 = input.substring(4, 6);
temp3 = input.substring(6);
Platform.runLater(new Runnable() {
@Override
public void run() {
myIndicator.setProgress(-1);
}
});
yearBuf.write(temp + "\n");
monthBuf.write(temp2 + "\n");
dayBuf.write(temp3 + "\n");
}
} else {
fWriter = new java.io.FileWriter(outputLocationFile + "\\" + ncFile.getName() + "_modified.csv", false);
writer = new BufferedWriter(fWriter);
String input;
String temp;
while ((input = br.readLine()) != null) {
temp = input.substring(0, 4) + "," + input.substring(4, 6) + "," + input.substring(6);
Platform.runLater(new Runnable() {
@Override
public void run() {
myIndicator.setProgress(-1);
}
});
writer.write(temp + "\n");
}
}
} catch (IOException e) {
e.printStackTrace(System.out);
}finally{
try{
if (br!=null) br.close();
if (yearBuf !=null) yearBuf.close();
if (monthBuf != null)monthBuf.close();
if (dayBuf != null)dayBuf.close();
if (yearWriter != null)yearWriter.close();
if (MonthWriter != null)MonthWriter.close();
if (DayWriter != null)DayWriter.close();
if (fWriter != null) fWriter.close();
if (writer != null) writer.close();
}catch(IOException e){
e.printStackTrace(System.out);
}
}
return true;
}
}
再说一次,没什么花哨的,一些缓冲流和编写器,就是这样!值得注意的是,这非常适合小/不是很大的文件。只有当面对数百万行文件时我才会看到这种行为。
如果您能提供任何帮助,我们将不胜感激,谢谢!
编辑 1
只是为了帮助澄清,if/else 的原因部分是尝试资源疯狂,而另一个是更传统的方式,只是为了举例说明它已经尝试过两种方式的事实,通过任一逻辑 block 运行的线程都会出现相同的症状,因此我相当确定关闭资源的方式与它无关。
最佳答案
编辑:并不是说我可以快速阅读代码。尝试这个。我之前错过了一些事情。 join() 只是等待它完成工作。我们需要稍后调用 stop() 或等效函数。 stop() 已被弃用,这就是为什么我仍然推荐线程池。 Executors.newCachedThreadPool()
应该可以解决问题。
Thread t=new Thread();
t.join();
t.stop();
旧的解决方案(也许有用): 确保线程终止的最简单方法是使用 Executors 和 ExecutorService
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(myRunnable);//execute right now
executorService.submit(myRunnable);//execute when <10 threads active
Future<MyType> future = executorService.submit(myCallable);//Runnable and Callable are efficient
MyType result = future.get();
executorService.submit(myThread);//more expensive to create threads and you are using a thread pool anyways
executorService.shutdown();//don't forget to do this when you are done executing or the program may hang
使用 Runnable 来简单地执行线程中的工作。当您需要返回结果时,请使用 Callable。
另一种方法是调用myThread.join();
第三种方法:SwingUtils.invokeLater(myRunnable);//最简单
编辑: 清理 try-catch 的解决方案: Java try/catch/finally best practices while acquiring/closing resources ..过度的解决方案,但很简单 Java io ugly try-finally block
关于java - 线程不会在 run() 结束时自然退出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24253298/