ReentrantReadWriteLockとAtomicIntegerの簡単な応用と比較
4401 ワード
背景の説明:
春節の切符を奪うシーンをシミュレートし、500スレッド(50同時)で100枚の切符を奪い、残りの切符が十分であれば切符を取得することに成功し、そうでなければ成功しない.
ReentrantReadWriteLock実装版:
AtomicInteger実装:
このとき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に入るという小さな問題もある.decrementAndGet()<0の場合、他のスレッドはAtomicIntegerを継続する.get()リクエストの場合、残量が-1の場合がありますが、ReentrantReadWriteLockではありませんので、実際の使用では注意してください.
実際の応用シーンに比べて、AtomicIntegerは変数を用いて計算する際に、よりよく機能することができる.データベースの操作など複雑なアプリケーションシーンでは、ReentrantReadWriteLockがより広く使用されています.
春節の切符を奪うシーンをシミュレートし、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に入るという小さな問題もある.decrementAndGet()<0の場合、他のスレッドはAtomicIntegerを継続する.get()リクエストの場合、残量が-1の場合がありますが、ReentrantReadWriteLockではありませんので、実際の使用では注意してください.
実際の応用シーンに比べて、AtomicIntegerは変数を用いて計算する際に、よりよく機能することができる.データベースの操作など複雑なアプリケーションシーンでは、ReentrantReadWriteLockがより広く使用されています.