多线程Java控制台应用程序在一台服务器上逐渐变慢

时间:2015-11-05 11:29:54

标签: java multithreading console

我有一个执行以下操作的多线程Java应用程序:

  1. 在主线程中递归迭代文件夹中的所有文件。
  2. 为每个文件启动一个新的线程(Runnable impl)
    1. 线程检查文件是否已转换。 (transform = copy + add extension + generate metadatafile)
    2. 如果是,则跳过并且线程结束。如果没有,则文件转换,之后线程结束。
  3. 我在许多机器和服务器上使用过这个应用程序,具有不同的最大线程(4-8-10-25),除了我们最新的服务器外,它总是运行得非常顺畅和快速。

    服务器信息:

    • 操作系统:Win2012 64位,HyperV VM
    • Java:JDK 1.7.0_80 64bit

    在这个特定的服务器上,应用程序快速启动,并在几分钟后逐渐减慢速度。示例:处理文档通常需要15毫秒(通过线程组合),现在转换文档30分钟后需要一秒钟,甚至跳过文档。

    我已经使用任务管理器和JVisualVM来检查发生了什么,但我无法确定问题。

    观察:

    • CPU在任何虚拟核心上几乎没有100%
    • RAM仅使用50%
    • 磁盘I / O似乎也没问题
    • McAfee不会扫描每个文档(我们已​​检查过)
    • Java以-Xms1024m -Xmx3072m启动,仅使用500MB的初始内存
    • PermGen没问题
    • 从JVisualVM手动收集垃圾邮件并没有采取任何措施来提高速度。
    • 立即重新启动应用程序会让它再次快速运行,但只需几分钟后再次放慢速度。
    • 我看到我的所有线程在JVisualVM中几乎总是处于“Park”状态。我不知道这是否正常。
    • 我们尝试在线程类中的finally块中添加LockSupport.unpark(Thread.currentThread())但是什么也没做。

    代码段:

    为每个文件启动线程:

    ThreadCountAwareThreadFactory threadFactory = ThreadCountAwareThreadFactory.getThreadFactory();
    executor = Executors.newFixedThreadPool(getMaxThreads(), threadFactory);
    
    //iterate files here and for each file do the following
    for(...) {
        ContentProvider contentProvider = springHelper.getBean(ContentProvider.class);          
        MetadataProvider metadataProvider = springHelper.getBean(MetadataProvider.class);
        ScopeSelector scopeSelector = springHelper.getBean(ScopeSelector.class);
    
        ThreadDataPackage dpkg = dataPackagesIterator.next();
        executor.execute(new TransformThread(dpkg, contentProvider, metadataProvider, scopeSelector, file, getOutputDir()));
    }
    
    //after all files have a thread launched
    try {
        executor.shutdown();
    
        executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        log.info(String.format("All threads (%s) completed...", threadFactory.getSpawnedThreadCount()));
    } catch(InterruptedException ie) {
        log.log(Level.SEVERE, "Error occurred when waiting for threads to complete...", ie);
    }
    

    TransformThread类(扩展BaseThread,它只是一个带有线程包的setter / getter的抽象类):

        public class TransformThread extends BaseThread {
    
        private File file; 
        private Logger log;
        private ContentProvider contentProvider;
        private MetadataProvider metadataProvider;
        private ScopeSelector scopeSelector;
        private String outputDir;
        private String identifier;
    
        public TransformThread(ThreadDataPackage dataPackage, ContentProvider contentProvider, 
            MetadataProvider metadataProvider, ScopeSelector scopeSelector, File file, String outputDir) {
            super(dataPackage);
            this.contentProvider = contentProvider;
            this.metadataProvider = metadataProvider;
            this.file = file;
            this.log = dataPackage.getLog();
            this.outputDir = outputDir;
            this.scopeSelector = scopeSelector;
            this.identifier = "TransformThread" + hashCode();
        }
    
        @Override
        public void run() {
                try {
                logInfo(">>>> Processing file " + file.getAbsolutePath(), null);
    
                //check if already processed
                if(isAlreadyProcessed(file)) {
                    logInfo(">>>> File was already processed, skipping...", null);
                    return;
                }
    
                contentProvider.setFilePath(file.getAbsolutePath());
    
                //process metadata
                logInfo(">>>> Processing metadata", null);
    
                //do some metadata processing and copy the file to the output directory, change extension, etc...
    
            } catch(Exception e) {
                logSevere("Error occurred during transform", e);
            Transform.addFailedFile(String.format("%s (%s)", file.getAbsolutePath(), e.getMessage()));
            } finally {
                LockSupport.unpark(Thread.currentThread());
            }
        }
    
        private void logInfo(String msg, Throwable error) {
            doLog(Level.INFO, msg, this.identifier, error);
        }
    
        private void logSevere(String msg, Throwable error) {
            doLog(Level.SEVERE, msg, this.identifier, error);
        }
    
        private void doLog(Level lvl, String msg, String identifier, Throwable error) {
            log.log(lvl, identifier + " - " + msg, error);
        }
    
        private boolean isAlreadyProcessed(File file) {
            return new File(file.getAbsolutePath() + Constants.PROCESSED_MARKER).exists();
        }
    
        protected void markProcessed(File file) 
                throws IOException {
            File processedMarker = new File(file.getAbsolutePath() + Constants.PROCESSED_MARKER);
            processedMarker.createNewFile();
        }
    
    }
    

    有没有人知道是什么导致该服务器的缓慢?

0 个答案:

没有答案