AtomicLong的incrementAndGet方法阻塞调用?

时间:2013-04-13 05:18:36

标签: java asynchronous atomicity concurrenthashmap

我正在研究Multithreaded code,我正在尝试测量特定方法花费的时间,因为我正在尝试对我们的大多数队友代码进行基准测试,因为我正在对我们进行Load and Performance测试Client code然后我们的Service code

所以对于这个性能测量,我正在使用 -

System.nanoTime();

我正在使用多线程代码从中生成多个线程并尝试测量代码所花费的时间。

下面是我尝试测量任何代码性能的示例示例 - 在下面的代码中我试图测量 -

beClient.getAttributes method

以下是代码 -

public class BenchMarkTest {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newFixedThreadPool(5);

        try {

            for (int i = 0; i < 3 * 5; i++) {
                executor.submit(new ThreadTask(i));
            }

            executor.shutdown();
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
        } catch (InterruptedException e) {

        }
    }

}

下面是实现Runnable接口的类

class ThreadTask implements Runnable {
    private int id;
    public static ConcurrentHashMap<Long, AtomicLong> selectHistogram = new ConcurrentHashMap<Long, AtomicLong>();


    public ThreadTask(int id) {
        this.id = id;
    }

    @Override
    public void run() {


        long start = System.nanoTime();

        attributes = beClient.getAttributes(columnsList);

        long end = System.nanoTime() - start;

        final AtomicLong before = selectHistogram.putIfAbsent(end / 1000000L, new AtomicLong(1L));
        if (before != null) {
            before.incrementAndGet();
        }
    }
}

无论我想测量什么代码,我通常将下面的行放在该方法的上方

long start = System.nanoTime();

这两行采用相同的方法但具有不同的ConcurrentHashMap

long end = System.nanoTime() - start;

final AtomicLong before = selectHistogram.putIfAbsent(end / 1000000L, new AtomicLong(1L));
        if (before != null) {
            before.incrementAndGet();
        }

今天我和我的一位资深人士会面了,他说incrementAndGet ConcurrentHashMap方法是一个阻塞电话。所以你的线程将在那里等待一段时间。

他让我做出Asynchronous call

是否有可能进行异步调用?

因为在我们所有的客户端代码和服务代码中测量每个方法的性能,我使用的是上面通常在每个方法之前和之后放置的三行来测量那些方法的性能。程序结束后,我将这些地图的结果打印出来。

所以现在我想考虑那个Asynchronous call?谁能帮助我做到这一点?

基本上,我试图以异步方式测量特定方法的性能,以便每个线程不会等待并被阻止。

我想,我可以使用Futures执行此操作。任何人都可以提供与此相关的示例吗?

感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

该行:

if (before != null) {
    before.incrementAndGet();
}

将锁定当前线程,直到before.incrementAndGet()获得锁定(如果您必须知道,实际上没有锁定,有while(true)compare-and-swap方法)并返回长值(你没有使用)。

您可以通过在自己的Thread中调用该特定方法使其异步,从而不会阻塞当前的Thread。

要做到这一点,我相信您已经知道如何:使用Thread.start()ExecutorServiceFutureTask(检查“How to asynchronously call a method in Java”如何在优雅的时尚)。

如果我不清楚,这是使用FutureTask的解决方案:

public class BenchMarkTest {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newFixedThreadPool(5);

        int threadNum = 2;
        ExecutorService taskExecutor = Executors.newFixedThreadPool(threadNum);
        List<FutureTask<Long>> taskList = new ArrayList<FutureTask<Long>>();

        try {

            for (int i = 0; i < 3 * 5; i++) {
                executor.submit(new ThreadTask(i, taskExecutor, taskList));
            }

            executor.shutdown();
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
        } catch (InterruptedException e) {

        }

        for (FutureTask<Long> futureTask : taskList) {
            futureTask.get(); // doing a job similar to joining threads
        }
        taskExecutor.shutdown();
    }

}

ThreadTask上课:

class ThreadTask implements Runnable {
    private int id;
    public static ConcurrentHashMap<Long, AtomicLong> selectHistogram = new ConcurrentHashMap<Long, AtomicLong>();

    private ExecutorService taskExecutor;
    private List<FutureTask<Long>> taskList;    

    public ThreadTask(int id, ExecutorService taskExecutor, List<FutureTask<Long>> taskList) {
        this.id = id;
        this.taskExecutor = taskExecutor;
        this.taskList = taskList;
    }

    @Override
    public void run() {


        long start = System.nanoTime();

        attributes = beClient.getAttributes(columnsList);

        long end = System.nanoTime() - start;

        final AtomicLong before = selectHistogram.putIfAbsent(end / 1000000L, new AtomicLong(1L));
        if (before != null) {
            FutureTask<Long> futureTask = new FutureTask<Long>(new Callable<Long>() {
                public Long call() {
                    return before.incrementAndGet();
                }
            });
            taskList.add(futureTask);
            taskExecutor.execute(futureTask);
        }
    }
}

更新

我想到了一些可能的改进:不要告诉taskExecutor执行futureTask类中的ThreadTask,最好将任务的执行推迟到最后main方法。我的意思是:

删除ThreadTask.run()下面的行:

            taskExecutor.execute(futureTask);

并且,在main()方法中,您拥有:

        for (FutureTask<Long> futureTask : taskList) {
            futureTask.get(); // doing a job similar to joining threads
        }
        taskExecutor.shutdown();

添加任务的执行,因此具有:

        taskExecutor.invokeAll(taskList);
        for (FutureTask<Long> futureTask : taskList) {
            futureTask.get(); // doing a job similar to joining threads
        }
        taskExecutor.shutdown();

(另外,您可以移除ThreadTask的{​​{1}}字段,因为它将不再使用它。)

这样,在执行基准测试时开销很小(开销是将对象添加到ExecutorService而没有其他内容。)

完整更新的代码:

taskList

-

public class BenchMarkTest {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newFixedThreadPool(5);

        List<FutureTask<Long>> taskList = new ArrayList<FutureTask<Long>>();

        try {

            for (int i = 0; i < 3 * 5; i++) {
                executor.submit(new ThreadTask(i, taskList));
            }

            executor.shutdown();
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
        } catch (InterruptedException e) {

        }

        int threadNum = 2;
        ExecutorService taskExecutor = Executors.newFixedThreadPool(threadNum);
        taskExecutor.invokeAll(taskList);
        for (FutureTask<Long> futureTask : taskList) {
            futureTask.get(); // doing a job similar to joining threads
        }
        taskExecutor.shutdown();
    }

}

答案 1 :(得分:0)

我确信递增AtomicLong所花费的时间比创建Runnable/Callable对象所花费的时间少,并将其传递给ExecutorService。这会导致你真正成为瓶颈吗?如果你真的想要快速并发增量,请参阅JDK8中的LongAdder

  

LongAdders可与ConcurrentHashMap一起使用,以维护可扩展的频率图(直方图或多重集的形式)。例如,要向ConcurrentHashMap freqs添加计数,初始化(如果尚未存在),可以使用freqs.computeIfAbsent(k - &gt; new LongAdder())。increment();