ReentrantReadWriteLock与AtomicInteger的简单应用与对比

背景说明:

  模拟春节抢票场景,500个线程(50并发)抢100张票,余票足够则成功取得票,否则不成功。

ReentrantReadWriteLock实现版:

public class ReadWriteLockTest {

	private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
	private static final WriteLock writeLock = readWriteLock.writeLock();
	private static final ReadLock readLock = readWriteLock.readLock();
	private static final ExecutorService pool = Executors.newFixedThreadPool(50);

	private int surplusTickets = 100;// 余票量
	private int surplusThread = 500;// 统计进程执行量,在进程都执行完毕后才关闭主线程

	/**
	 * 运行多线程,进行模拟抢票,并计算执行时间
	 */
	@Test
	public void testReadLock() {
		Date beginTime = new Date();
		for (int i = 0; i < surplusThread; i++) {
			final int runNum = i;
			pool.execute(new Runnable() {
				public void run() {
					boolean getted = takeTicket();

					String gettedMsg = "";
					if (getted) {
						gettedMsg = "has getted";
					} else {
						gettedMsg = "not getted";
					}
					System.out.println("thread " + runNum + " " + gettedMsg + ", remain: " + surplusTickets
							+ ", line up:" + surplusThread + "..");
				}
			});
		}

		while (surplusThread >= 30) {
			sleep(100);
		}
		Date overTime = new Date();
		System.out.println("take times:" + (overTime.getTime() - beginTime.getTime()) + " millis.");
	}

	/**
	 * 查询当前的余票量
	 */
	private int nowSurplus() {
		readLock.lock();
		int s = this.surplusTickets;
		sleep(30);// 模拟复杂业务
		readLock.unlock();
		return s;
	}

	/**
	 * 拿出一张票
	 */
	private boolean takeTicket() {
		writeLock.lock();
		boolean result = false;

		if (nowSurplus() > 0) {
			this.surplusTickets -= 1;
			result = true;
		}

		surplusThread -= 1;
		writeLock.unlock();
		return result;
	}

	/**
	 * 睡觉觉
	 */
	private void sleep(int millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

AtomicInteger实现版:

public class AtomicIntegerTest {

	private static final ExecutorService pool = Executors.newFixedThreadPool(50);

	private AtomicInteger surplusTickets = new AtomicInteger(100);// 余票量
	private int surplusThread = 500;// 统计进程执行量,在进程都执行完毕后才关闭主线程

	/**
	 * 运行多线程,进行模拟抢票,并计算执行时间
	 */
	@Test
	public void testReadLock() {
		Date beginTime = new Date();
		for (int i = 0; i < surplusThread; i++) {
			final int runNum = i;
			pool.execute(new Runnable() {
				public void run() {
					boolean getted = takeTicket();

					String gettedMsg = "";
					if (getted) {
						gettedMsg = "has getted";
					} else {
						gettedMsg = "not getted";
					}
					System.out.println("thread " + runNum + " " + gettedMsg + ", remain: " + surplusTickets
							+ ", line up:" + surplusThread + "..");
				}
			});
		}

		while (surplusThread >= 30) {
			sleep(100);
		}
		Date overTime = new Date();
		System.out.println("take times:" + (overTime.getTime() - beginTime.getTime()) + " millis.");
	}

	/**
	 * 拿出一张票
	 */
	private boolean takeTicket() {
		boolean result = false;
		sleep(30);
		if (surplusTickets.decrementAndGet() >= 0) {
			result = true;
		} else {
			surplusTickets.set(0);
		}
		surplusThread -= 1;
		return result;
	}

	/**
	 * 睡觉觉
	 */
	private void sleep(int millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

此时sleep(30 millis)模拟复杂业务,二者执行时间分别为:

    ReentrantReadWriteLock实现版 14904 millis, AtomicInteger实现版 402 millis.

当把sleep(30 millis)移动到takeTicket()之外后,ReentrantReadWriteLock实现版 执行时间为:410 millis.

当取消sleep(30 millis)模拟复杂业务时,二者执行时间分别为:

    ReentrantReadWriteLock实现版 116 millis, AtomicInteger实现版 103 millis.


  以上比较可以看出,当ReentrantReadWriteLock为较为复杂的业务加锁时,执行时间将较大差距于AtomicInteger.decrementAndGet();而当ReentrantReadWriteLock为简单业务加锁时,执行时间仅仅略慢于AtomicInteger.decrementAndGet(),且几乎忽略不计。可见,ReentrantReadWriteLock的执行时间将取决于加锁的业务范围,而AtomicInteger则只关注关键操作,使用ReentrantReadWriteLock时应尽量缩小加锁的范围,否则效率上将不如于AtomicInteger。

  同时,在使用中也出现AtomicInteger的一点小问题,就是当某线程进入AtomicInteger.decrementAndGet() < 0的情况时,其他线程继续AtomicInteger.get()请求的话,会出现余量为-1的情况,ReentrantReadWriteLock则不会,在实际使用中应注意这种情况。

  相对于实际应用场景来说,AtomicInteger在使用变量进行计算时,可以较好地发挥作用;而在操作数据库等复杂的应用场景中,ReentrantReadWriteLock则显得使用范围更广。

你可能感兴趣的:(ReentrantReadWriteLock与AtomicInteger的简单应用与对比)