情景:用sqlldr导入数据入库时会产生一些日志,在入库未完成时,日志是大小为0的空文件,能通过日志大小判断是否入库完成

/**
     * 等待文件有数据且已写完
     * 费时操作 放在子线程中执行
     *
     * @param file  文件
     * @param start 需要传入 System.currentTimeMillis(),用于兼容遍历的情况
     * @return true:已写完 false:外部程序阻塞或者文件不存在
     */
    public boolean isFileCompleted(File file, long start) {
        if (!file.exists())
            return false;

        long fileLength = 0;
        int i = 0;
        logger.info("正在根据日志文件大小监测数据文件是否入库完成(入库成功与否请等待入库完成后查看本程序输出的日志报告), 请稍候...");
        while (true) {
            //文件在外部一直在填充数据,每次进入循环体时,文件大小都会改变,一直到不改变时,说明文件数据填充完毕 或者文件大小一直都是0(外部程序阻塞)
            //判断文件大小是否有改变
            if (file.length() > fileLength) {  //有改变说明还未写完
                fileLength = file.length();

                if (i % 120 == 0) { //每隔1分钟输出一次日志 (i为120时:120*500/1000=60秒)
                    logger.info("日志文件: " + file.getName() + " 正在被填充,请稍候...");
                }

                try {
                    Thread.sleep(500);//半秒后再循环一次
                } catch (InterruptedException e) {
                    logger.error(e.getMessage(), e);
                }
            } else { //否则:只能等于 不会小于,等于有两种情况,一种是数据写完了,一种是外部程序阻塞了,导致文件大小一直为0
                if (file.length() != 0) {
                    //被填充完成则立即输出日志
                    logger.info("日志文件: " + file.getName() + " 被填充完成,即将输出该日志报告");
                    break;//写完了 退出当前循环体 执行下面的 return true
                } else { //等待外部程序开始写 只等60秒 120*500/1000=60秒

                    //每隔1分钟输出一次日志 (i为120时:120*500/1000=60秒)
                    if (i % 120 == 0) {
                        logger.info("日志文件: " + file.getName() + " 大小为0,正在等待外部程序填充,已等待:" + (System.currentTimeMillis() - start) + "毫秒");
                    }

                    //如果一直(i为120时:120*500/1000=60秒)等于0,说明外部程序阻塞了
                    if (i == 3600) { //120为1分钟 3600为30分钟
                        logger.info("日志文件: " + file.getName() + " 大小在 :"
                                + (System.currentTimeMillis() - start) + "毫秒内始终为0,说明:在 程序监测时间内 入库进程依旧在进行 ,程序监测时间结束");//入库未完成或发生阻塞
                        return false;
                    }

                    //等待外部程序开始写
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        logger.error(e.getMessage(), e);
                    }
                }
            }
            i++;
        }
        return true;
    }

调用示例:

isFileCompleted(new File("xxx.log"), System.currentTimeMillis())

注:

为什么不用以下代码来判断是否入库完成?:

int result = process.waitFor();

原因:

在 Linux 下调用外部命令(Shell命令),或者可执行的二进制文件,
Java 中依赖 Process 和 Runtime 两个类,查看官方API:
    Process 抽象类中是通过 ProcessBuilder.start() 和 Runtime.exec 两个方法来创建本地进程的,
    创建的子进程是没有他们自己的 terminal 或者 console 的。
    所有标准的 I/O 都会重定向到父进程,
    并且通过方法 getOutStream(), getInputStream(), 和 getErrorStram(), 
    分别获得标准输出,输入和错误流。
    由于 JVM 提供标准输入和输出流的缓冲区大小十分有限,
    很快的读入或者输出流将会导致进程阻塞或者锁死