有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

arrays Java返回ArrayList的速度慢吗?

我从一个对象返回一个数组列表,并在另一个对象中使用它。应用程序是多线程的,每个线程从一个文件中一次填充一个int,因此每次添加都是对数组列表的访问。有200个线程,每个线程的文件大小为100万整数。该应用程序需要数小时才能运行,我认为这是我的瓶颈,因为当我使用本地数组列表进行测试时,需要4分钟。我的问题是,这在任何地方都被使用,我需要在数组列表上同步。这个问题有没有快速的解决方案,或者我必须让每个线程都有自己的数组列表而不返回它

事实上我错了,只有当数组是本地的,并且在任何地方都比在类的顶端声明的更快时,运行起来才需要几个小时,我对此感到困惑

我的返回代码如下所示:

synchronized public ArrayList<Integer> getData() 
{
    return this.myData;
}

以下是运行缓慢的内容,我删除了其他内容,并试图以此为基准,这需要几个小时:

    Scanner scanner = new Scanner(filePath);

    /*
     * While we have data keep reading
     * when out of data the simulation is complete.
     */
    while (scanner.hasNext()) 
    {
        /*
         * Get the data to simulate requests
         * and feed it to the algorithm being evaluated.
         */
        if (scanner.hasNextInt()) 
        {
            int temp = scanner.nextInt();
            //System.out.println( this.tClientName+" "+temp);


            /*
             * Add the temp value from incoming stream. 
             * 
             * todo:: UNLESS its NOT found on the client as a miss
             */
            tClientCache.getCache().add(temp); 

        } 
        else 
        {
            scanner.next();
        }
    }//END Of while (scanner.hasNext()) 
    /*
     * Close the scanner
     */
    scanner.close();

共 (2) 个答案

  1. # 1 楼答案

    问题几乎肯定不是返回ArrayList的行为,因为这只是返回一个引用

    最有可能的情况是同步开销,因为对该方法的每次调用都需要获取锁,获取数据,然后释放锁(有一些警告,但基本上是这样的)

    此外,几乎可以肯定的是,同步甚至不能实现您希望它做的事情,因为对ArrayList的实际访问需要同步,而不仅仅是获取对它的引用

    一般来说,你有两个选择:

    • 减少同步点的数量(即减少同步频率)
    • 选择更高效的同步机制

    你的线程是否可以收集大量结果并将其成批(比如一次一千个)?或者您可以切换到一个更支持多线程的数据结构(我想到了CopyOnWriteArrayList,但它针对频繁读取和非常不频繁写入进行了优化,所以可能不适用于您的用例)

  2. # 2 楼答案

    如果并发函数如下所示:

    Scanner scanner = new Scanner(filePath);
    
    while(scanner.hasNext()) {
        if(scanner.hasNextInt()) {
            int temp = scanner.nextInt();
            tClientCache.getCache().add(temp);
        } else {
            scanner.next();
        }
    }
    
    scanner.close();
    

    您可以使用通用同步对象进行同步:

    Scanner scanner = new Scanner(filePath);
    Object syncObject = tClientCache.getSynchronizationObject();
    ArrayList<Integer> list = tClientCache.getCache();
    
    while(scanner.hasNext()) {
        if(scanner.hasNextInt()) {
            int temp = scanner.nextInt();
            // synchronise manipulation
            synchronized(syncObject) {
                list.add(temp);
            }
        } else {
            scanner.next();
        }
    }
    
    scanner.close();
    

    并通过以下方式扩展CacheClient

    class CacheClient {
         ...
         public Object getSynchronizationObject() { return m_syncObj; }
         ...
         private Object m_syncObj = new Object(); // For synchronised access to the cache.
    }
    

    当然,在添加到列表时,您还必须同步对缓存的所有其他访问。考虑以这样的方式重写程序,即每一个文件的输出都是独立处理的,因此每个都在自己的(非同步)列表中,或者在需要合并数据的情况下,处理数据:

    Scanner scanner = new Scanner(filePath);
    int threshold = ...
    
    while(scanner.hasNext()) {
        if(scanner.hasNextInt()) {
            int temp = scanner.nextInt();
            bulk.add(temp);
            // instead of an arbitrary threshold, why not merge the array of a whole file?
            if(bulk.size() >= threshold) {
                tClientCache.process(bulk);
                bulk.clear();
            }
        } else {
            scanner.next();
        }
    }
    if(!bulk.isEmpty()) {
        tClientCache.process(bulk);
    }
    
    scanner.close();
    

    并在^{中执行同步:

    class ClientCache {
        ...
        public void process(ArrayList<Integer> bulk) {
            // synchronise cache manipulation
            synchronized(getSynchronizationObject()) {
                // merge howsoever you like...
                getCache().addAll(bulk);
            }
        }
    }
    

    对于当前系统(<;1GB),2亿int不是很多数据,但2亿Integerabout 3 GB!取决于您对该数据所做的处理,内存访问可能会完全破坏您的性能:再次,尽可能执行大容量数据处理,如果需要进行排序之类的高性能数据,请考虑将数据块复制到固定大小的^ {< CD5> },在基本类型数组上执行排序。然后再将这些块体合并回阵列中