AtomicLong と ReentrantReadWriteLock の速度比較

int などへのアクセスとは異なり、long へのアクセスはアトミックな処理ではないため、マルチスレッド環境では排他処理が必要になります。普通に処理排他処理をするのであれば ReentrantReadWriteLock を使いそうなものですが、純粋に long のみをアトミックに扱いたいだけであれば、AtomicLong が使えます。しかし、性能がどの程度異なるのでしょうか。ということで、ベンチマークしてみました。

処理概要

  • long への read と write を交互に合計100万回行うスレッドを10スレッド並行動作させた時間を計測。
  • long のテストは、long をフィールドとして持つクラスを作成し、get では ReadLock を使用、set では WriteLock を使用。
  • AtomicLong のテストは、AtmicLong#get() と AtomicLong#set() を使用。

結果

long+ReentrantReadWriteLock AtomicLong
1610 ms 391 ms

さすがにネイティイブメソッドを駆使してるだけあって、AtomicLong は高速ですね。

ソース

ソース

package jp.objectfanatics.ofcache.misc.atomic_long_test;

import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * AtomicLongBenchmark
 * 
 * @author beyondseeker
 * 
 * $id$
 *
 */
public class AtomicLongBenchmark {
	
	public static void main(String[] args) throws Exception {
		test01();
		test02();
	}
	
	public static void test01() throws Exception {
		System.out.println("test01");
		final int threadNum = 10;
		final int count = 1000000;
		
		final AtomicLong l = new AtomicLong();
		
		Thread[] threads = new Thread[threadNum];
		for (int i = 0; i < threadNum; i++) {
			threads[i] = new Thread() {
				public void run() {
					for (int i = 0; i < count; i++) {
						if (i % 2 == 0) {
							l.set((long) i);
						} else {
							l.get();
						}
					}
				}
			};
		}
		
		// execute and take the execution time
		long start = System.currentTimeMillis();
		for (int i = 0; i < threads.length; i++)
			threads[i].start();
		for (int i = 0; i < threads.length; i++)
			threads[i].join();
		long end = System.currentTimeMillis();
		System.out.println(end - start);
	}
	
	public static void test02() throws Exception {
		System.out.println("test02");
		final int threadNum = 10;
		final int count = 1000000;
		
		final LongContainer l = new LongContainer();
		
		Thread[] threads = new Thread[threadNum];
		for (int i = 0; i < threadNum; i++) {
			threads[i] = new Thread() {
				public void run() {
					for (int i = 0; i < count; i++) {
						if (i % 2 == 0) {
							l.setL((long) i);
						} else {
							l.getL();
						}
					}
				}
			};
		}
		
		// execute and take the execution time
		long start = System.currentTimeMillis();
		for (int i = 0; i < threads.length; i++)
			threads[i].start();
		for (int i = 0; i < threads.length; i++)
			threads[i].join();
		long end = System.currentTimeMillis();
		System.out.println(end - start);
	}
	
	private static class LongContainer {
		private long l;
		
		private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
		
		public long getL() {
			lock.readLock().lock();
			long result = l;
			lock.readLock().unlock();
			return result;
		}
		
		public void setL(long l) {
			lock.writeLock().lock();
			this.l = l;
			lock.writeLock().unlock();
		}
	};
}