Java多线程同步求助:如何阻止线程在Excel操作完成前启动?
问题分析
你当前的代码存在两个核心问题:
- 线程任务与实际操作脱节:
MyRunnable的run方法只是简单sleep,和创建Excel、打开Excel、获取文件大小的实际操作完全无关。比如t2线程启动后,4秒就结束了,但此时Excel还处于打开状态,主线程的t2.join()自然无法等待Excel关闭。 - 关键操作未绑定到对应线程:打开Excel的
excelFileHandler.openSpreadSheet调用是在主线程中执行的,而非t2线程,导致t2线程根本不负责等待Excel关闭的逻辑。
解决方案
要实现“等待Excel关闭后再启动获取文件大小的线程”,需要做到两点:
- 让负责打开Excel的线程(t2)真正等待Excel进程终止。
- 主线程必须在t2完全结束(即Excel关闭)后,再启动t3线程。
具体步骤:
- 修改
ExcelFileHandler.openSpreadSheet方法,使其返回启动的Excel进程对象,这样可以在代码中等待进程结束。 - 为每个任务编写独立的
Runnable实现,避免通用MyRunnable的无效逻辑。 - 主线程中严格按顺序:启动t1→等待t1完成→启动t2→等待t2完成(Excel关闭)→启动t3→等待t3完成。
修改后的代码示例
首先调整ExcelFileHandler的openSpreadSheet方法(假设原来的实现是通过Runtime.getRuntime().exec打开Excel):
import java.io.File; import java.io.IOException; public class ExcelFileHandler { public void createExcelFile(String fileName) throws IOException { // 原有的POI创建Excel逻辑,此处省略 File file = new File(fileName); if (!file.exists()) { file.createNewFile(); // 这里添加POI写入空Excel的代码 } } public Process openSpreadSheet(String fileName) throws IOException { // 启动Excel进程并返回该进程,/wait参数让命令行等待Excel关闭 String command = "cmd /c start /wait excel.exe \"" + new File(fileName).getAbsolutePath() + "\""; return Runtime.getRuntime().exec(command); } public long getFileSize(String fileName) { File file = new File(fileName); return file.exists() ? file.length() : -1; } }
然后编写独立的任务Runnable:
// 创建Excel文件的任务 class CreateFileRunnable implements Runnable { private final ExcelFileHandler handler; private final String fileName; public CreateFileRunnable(ExcelFileHandler handler, String fileName) { this.handler = handler; this.fileName = fileName; } @Override public void run() { System.out.println("线程t1启动:创建Excel文件"); try { handler.createExcelFile(fileName); } catch (IOException e) { e.printStackTrace(); } System.out.println("线程t1结束:Excel文件创建完成"); } } // 打开Excel并等待关闭的任务 class OpenExcelRunnable implements Runnable { private final ExcelFileHandler handler; private final String fileName; public OpenExcelRunnable(ExcelFileHandler handler, String fileName) { this.handler = handler; this.fileName = fileName; } @Override public void run() { System.out.println("线程t2启动:打开Excel文件"); try { Process excelProcess = handler.openSpreadSheet(fileName); // 等待Excel进程终止(即用户关闭Excel) excelProcess.waitFor(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } System.out.println("线程t2结束:Excel已关闭"); } } // 获取文件大小的任务 class GetFileSizeRunnable implements Runnable { private final ExcelFileHandler handler; private final String fileName; public GetFileSizeRunnable(ExcelFileHandler handler, String fileName) { this.handler = handler; this.fileName = fileName; } @Override public void run() { System.out.println("线程t3启动:获取Excel文件大小"); long size = handler.getFileSize(fileName); System.out.println("Excel文件大小:" + size + " 字节"); System.out.println("线程t3结束"); } }
最后修改主线程逻辑:
public class ExcelFileCreator { private static final ExcelFileHandler excelFileHandler = new ExcelFileHandler(); private static final String FILE_NAME = "coggis.xlsx"; public static void main(String[] args) { Thread t1 = new Thread(new CreateFileRunnable(excelFileHandler, FILE_NAME), "t1"); Thread t2 = new Thread(new OpenExcelRunnable(excelFileHandler, FILE_NAME), "t2"); Thread t3 = new Thread(new GetFileSizeRunnable(excelFileHandler, FILE_NAME), "t3"); try { // 启动t1,等待创建完成 t1.start(); t1.join(); // 启动t2,等待Excel关闭 t2.start(); t2.join(); // 启动t3,获取文件大小 t3.start(); t3.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("所有任务完成"); } }
关键说明
openSpreadSheet的start /wait参数:在Windows命令中,start /wait会让命令行等待启动的Excel进程结束后再继续,这样Process.waitFor()就能正确捕获Excel关闭的事件。- 线程顺序执行:通过
join()方法严格保证t1完成后才启动t2,t2完成(Excel关闭)后才启动t3,彻底解决了t3提前启动的问题。 - 任务解耦:每个Runnable只负责单一任务,代码逻辑更清晰,也便于维护和调试。
内容的提问来源于stack exchange,提问作者Rusty
相关产品推荐
相关产品推荐

