java - 哪种方法更好 - 多线程示例

标签 java multithreading performance concurrency memory-efficient

我想知道这两种在 Java 7 中使用多线程执行任务的方法之间是否有任何区别:

简要要求 - 我必须同时处理 200 条记录,并且每条记录只能处理一次。

方法一:

public class Counter {

    private static int count = -1;
    private static final Object lock = new Object();

    public static int getCount() {
        synchronized (lock) {
            return ++count;
        }
    }
}



public class Task implements Runnable {

    @Override
    public void run() {
        int count = 0;
        while((count = Counter.getCount()) < 200) {
            System.out.println(Thread.currentThread().getName() + " " + count);
            // process record # count
        }
    }
}

public class Tester {

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        ExecutorService service = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            service.execute(new Task());
        }
        service.shutdown();
        service.awaitTermination(5, TimeUnit.MINUTES);
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

方法 2:

public class Task2 implements Runnable {

    private int i = 0;

    public Task2(int i) {
        super();
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " " + i);
        // process record # i
    }
}

public class Tester2 {

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        ExecutorService service = Executors.newFixedThreadPool(10); 
        for (int i = 0; i < 200; i++) {
            service.execute(new Task2(i));
        }
        service.shutdown();
        service.awaitTermination(5, TimeUnit.MINUTES);
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

两者都运行良好并产生相同的输出并且花费几乎相同的时间。 然而,示例代码中的 200 个数字在实际场景中将达到数十亿。请建议哪一个在内存和CPU等方面更好。

谢谢。

编辑 - 根据 Jimmy T. 的建议添加方法 3。

方法 3:

public class Tester3 {

    public static void main(String[] args) throws InterruptedException {

        int processedRecords = 0;
        int totalRecords = 200;
        int recordsPerThread = 10;
        boolean continueProcess = true;

        ExecutorService service = Executors.newFixedThreadPool(10);

        while(continueProcess) {
            int startIndex = processedRecords;
            int endIndex = startIndex + recordsPerThread - 1;
            if (endIndex >= totalRecords - 1) {
                endIndex = totalRecords - 1;
                continueProcess = false;
            }
            processedRecords = processedRecords + recordsPerThread;
            service.submit(new Task3(startIndex, endIndex));
        }
        service.shutdown();
        service.awaitTermination(5, TimeUnit.MINUTES);
    }
}


public class Task3 implements Runnable {

    private int startIndex = 0;
    private int endIndex = 0;

    public Task3(int startIndex, int endIndex) {
        super();
        this.startIndex = startIndex;
        this.endIndex = endIndex;
    }

    @Override
    public void run() {
        System.out.println("processing records from " + startIndex + " to " + endIndex);
    }
}

最佳答案

Brief requirement - I have to process 200 records, simultaneously, and each record should be processed once only.

However 200 number taken in example code will be in billions in actual scenario.

一如既往,答案是“视情况而定”。除非您的任务非常快,否则同步或对象创建的开销应该不重要。请注意,ExecutorService 也必须在内部进行一些同步。

您可以使用 AtomicInteger 计数器来避免同步并简化代码。但我很可能会选择一个 Queue 和几个 Runnable 来轮询它,因为它很简单并且为您提供了很大的灵活性:

  • 如果您想确定某些记录的优先级,可以使用 PriorityQueue
  • 如果将所有记录存储在内存中成本太高,您可以创建自己的队列(您的队列可以从文件或数据库或其他任何地方获取它们)

您可以预先填充队列或使用由生产者填充的BlockingQueue。队列的内容可以是记录索引或(可能更好)记录本身。

每个任务都会轮询队列并在队列为空时终止。

public class Task3 implements Runnable {
    @Override public void run() {
        while (true) {
            final Integer i = queue.poll();
            if (i==null) break;    
            System.out.println(Thread.currentThread().getName() + " " + i);
            // process record # i
        }
    }
}

您只需创建有限数量的它们即可让您的所有核心保持忙碌。

ExecutorService service = Executors.newFixedThreadPool(10); 
for (int i = 0; i < 10; i++) service.execute(new Task3());

关于java - 哪种方法更好 - 多线程示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25534751/

相关文章:

java - 从 html (JSOUP) 获取图标

Python 多处理和套接字未关闭

java - 检查两个矩形是否重叠

java - 奇怪的 Oracle 连接 URL

C++ 线程 - 在循环中生成一个对象(类)?

java - 如何使用 Java 将 >1000 个 xml 文件合并为一个文件

python - 选择查询非常慢,我怎样才能加快速度?

python - 如何打印每秒迭代次数?

java - 如何在eclipse中自动重新加载java类?

c# - 在 C# 中从线程更新 UI 的其他方法