一度に 1 人の顧客に対して複数の操作を処理できないように、マップを使用してスレッド ブロックを実装しようとしています。これは Web サービスと通信しており、ワークフロー全体を完了するには複数の手順が必要です。一度に 1 人の顧客をロックできるようにする必要がありますが、フローをブロックすることなく他のスレッドを実行できるようにする必要があります。
これを機能させる方法を確認するための私のテストケースを次に示します。私が見ているのはdoSynchronizedSomething
、最初のスレッドがクリアされるまで、2 番目のスレッドが同期ブロックに入ることができないということです。これでうまくいくはずだと思ったのですが、期待どおりに動作していません。
これが結果で、ミリ秒が 3 秒離れていることがわかります。CustomerLocks
また、テストケースで同じオブジェクトではないことを確認しました。これは可能ですか?
Starting operation 123456 at time 1381173121688
Done with operation for 123456 at time 1381173124689
Starting operation 234567 at time 1381173124689
Done with operation for 234567 at time 1381173127690
コード
package simplethreadlock;
public class CustomerLock {
private String customerId;
public CustomerLock(String customerId) {
}
public String getCustomerId() {
return customerId;
}
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
}
package simplethreadlock;
import java.util.concurrent.ConcurrentHashMap;
public class CustomerLockingMap {
private static ConcurrentHashMap<String, CustomerLock> locks = new ConcurrentHashMap<String, CustomerLock>();
public static CustomerLock aquireLock(String customerId) {
CustomerLock lock = locks.get(customerId);
if (lock == null) {
lock = new CustomerLock(customerId);
locks.put(customerId, lock);
}
return lock;
}
}
package simplethreadlock;
import org.junit.Assert;
import org.junit.Test;
public class CutomerLockingTest {
@Test
public void testLock() throws InterruptedException {
final String customerId1 = "123456";
final String customerId2 = "234567";
final CustomerLock customer1Lock1 = CustomerLockingMap
.aquireLock(customerId1);
final CustomerLock customer1Lock2 = CustomerLockingMap
.aquireLock(customerId1);
final CustomerLock customer2Lock1 = CustomerLockingMap
.aquireLock(customerId2);
final CustomerLock customer2Lock2 = CustomerLockingMap
.aquireLock(customerId2);
CountDownLatch latch = new CountDownLatch(1);
Assert.assertNotEquals(customer1Lock1, customer2Lock1);
new Thread(new Runnable() {
public void run() {
try {
doSynchronziedSomething(customer1Lock1, customerId1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
doSynchronziedSomething(customer2Lock1, customerId2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
doSynchronziedSomething(customer1Lock2, customerId1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
doSynchronziedSomething(customer2Lock2, customerId2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
latch.await(8, TimeUnit.SECONDS);
}
private void doSynchronziedSomething(final CustomerLock lock, final String customerId) throws InterruptedException {
synchronized (lock) {
System.out.println("Starting operation " + customerId + " at time "
+ System.currentTimeMillis());
Thread.sleep(3000);
System.out.println("Done with operation for " + customerId
+ " at time " + System.currentTimeMillis());
}
}
}
編集
愚かな私は Thread.start() ですが、これに関するヘルプの例を見ている場合は、スレッドが終了する前に単体テストが終了しないように、CountDownLatch を追加しました。