在我的项目与Ehcache(使用BlockingCache装饰器,内部使用ReentrantLock)进行集成期间,我在一台机器上发现了一些奇怪的行为。有时候,通过ReentrantLock.tryLock(超时,TimeUnit)等待锁定获取的线程不会被超时(带有“假”结果)。 JVM将它们抛在了后面,它们被阻塞,直到另一个线程释放了这个锁。简而言之:ReentrantLock.tryLock(长超时,TimeUnit单位)的行为与ReentrantLock.lock()不同。
这种情况只发生在一台机器上,带有规范:
我在jre 1.6(1.6.0_31,1.6.0_37)上运行它
我在其他2台机器上进行了测试,它运行良好(这个奇怪的问题无法在那里重现)。
这是我的测试代码: 它启动第二个线程,持有锁定5秒。主线程尝试获取它(timout = 50ms)。有时(它在机器上1000次迭代时会断开大约14次)主线程在50ms后不会超时,而是等到第二个线程释放锁定(= 5s)。
@Test
public void test() throws Exception {
System.out.println("debug___");
int failCount = 0;
final int REPEAT_TIMES = 50 * 1000;
for (int i = 0; i < REPEAT_TIMES; i++) {
System.out.println("");
try {
testOne(i);
System.out.println("debug___" + i + ":ok");
} catch (Throwable e) {
failCount++;
System.err.println(i + ":" + e.getMessage());
System.out.println(i + ":" + e.getMessage());
System.out.println("debug___" + i + ":fail");
}
}
System.out.println("fails:" + failCount);
Assert.assertEquals("Failed " + failCount + "/" + REPEAT_TIMES, 0, failCount);
}
public void testOne(final int i) throws Exception{
final StringBuilder sb = new StringBuilder();
try {
final ReentrantLock lock = new ReentrantLock();
final Semaphore s = new Semaphore(0);
Thread t1 = new Thread() {
@Override
public void run() {
try {
sb.append(i + ":" + 1).append("\n");
boolean locked = lock.tryLock(1000, TimeUnit.MILLISECONDS);
sb.append(i + ":" + 2 + " " + locked).append("\n");
s.release();
sb.append(i + ":" + 3).append("\n");
Thread.sleep(5000); // !!! release lock after 5s
sb.append(i + ":" + 4 + " \n");
lock.unlock();
Thread.sleep(2000);
} catch (Throwable e) {
e.printStackTrace();
}
}
};
t1.start();
sb.append(i + ":" + "m1").append("\n");
s.acquire();
sb.append(i + ":" + "m2").append("\n");
long start = System.currentTimeMillis();
boolean tryLock = lock.tryLock(50, TimeUnit.MILLISECONDS); // try acquire lock for 50ms
long stop = System.currentTimeMillis();
long diff = stop - start;
if (tryLock)
throw new IllegalStateException("it is really bad.... blocked for a [ms]:" + diff);
} finally {
System.out.print(sb.toString());
}
}
感谢您的任何建议!
更新
这是第二次阻止机器的测试:
import java.util.concurrent.locks.*;
import java.util.concurrent.*;
public class TestLock2 {
final ReentrantLock lock = new ReentrantLock();
volatile int inx = 0;
public static void main(String[] a) throws Exception {
TestLock2 t = new TestLock2();
t.test();
}
public void log(String s) {
System.out.println(s);
System.err.println(s);
}
public void test() throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
lock.lock();
System.out.println("ok");
while (true) {
try {
Thread.sleep(60 * 1000);
log(inx
+ " "
+ new java.util.Date());
} catch (Exception e) {
}
}
}
};
t1.start();
Thread.sleep(500); // wait for lock to be locked
System.out.println("debug___");
int failCount = 0;
final int REPEAT_TIMES = 20000;
for (int i = 0; i < REPEAT_TIMES; i++) {
try {
inx = i;
testOne(i);
} catch (Throwable e) {
failCount++;
log(i + ":" + e.getLocalizedMessage());
log("debug___" + i + ":fail");
}
}
log("Failed " + failCount + "/" + REPEAT_TIMES);
}
public void testOne(final int i) throws Exception {
long start = System.currentTimeMillis();
try {
boolean tryLock = lock.tryLock(200, TimeUnit.MILLISECONDS);
} finally {
long diff = System.currentTimeMillis() - start;
log(i + ":" + diff + "ms");
}
}
}
,输出
0:203ms 1:203ms 2:203ms 3:203ms 4:203ms 5
太阳5月26日10:36:22 EDT 2013 5
太阳5月26日10:37:22 EDT 2013 5
Sun May 26 10:38:22 EDT 2013 ..................... // 20小时后 (在这里砍伐日志)5
Mon May 27 06:00:26 EDT 2013 5
Mon May 27 06:01:26 EDT 2013 5
Mon May 27 06:02:26 EDT 2013 5
2013年5月27日星期一06:03:26
和Thread Dump:
2013-05-27 04:51:51全线程转储Java HotSpot(TM)客户端VM (20.6-b01混合模式,分享):
“Thread-0”prio = 6 tid = 0x02b78400 nid = 0xdf0等待条件 [0x02ebf000] java.lang.Thread.State:TIMED_WAITING(sleep)at java.lang.Thread.sleep(Native Method)at TestLock2 $ 1.run(TestLock2.java:26)
“低内存检测器”守护进程prio = 6 tid = 0x02b5a400 nid = 0x218c可运行 [0x00000000] java.lang.Thread.State:RUNNABLE
“C1 CompilerThread0”守护进程prio = 10 tid = 0x02b55400 nid = 0x7a4等待 条件[0x00000000] java.lang.Thread.State:RUNNABLE
“Attach Listener”守护进程prio = 10 tid = 0x02b52c00 nid = 0x2450等待 condition [0x00000000] java.lang.Thread.State:RUNNABLE
“Signal Dispatcher”守护程序prio = 10 tid = 0x02b51800 nid = 0x11d4 runnable [0x00000000] java.lang.Thread.State:RUNNABLE
“Finalizer”守护程序prio = 8 tid = 0x02b4c000 nid = 0x13bc在Object.wait()中 [0x02cdf000] java.lang.Thread.State:WAITING(在对象监视器上) at java.lang.Object.wait(Native Method) - 等待&lt; 0x22991148&gt; (java.lang.ref.ReferenceQueue $ Lock)java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118) - 已锁定&lt; 0x22991148&gt; (java.lang.ref.ReferenceQueue $ Lock)java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134)at java.lang.ref.Finalizer $ FinalizerThread.run(Finalizer.java:159)
“参考处理程序”守护程序prio = 10 tid = 0x02b47400 nid = 0x2634 in Object.wait()[0x02c8f000] java.lang.Thread.State:WAITING(on 对象监视器)在java.lang.Object.wait(本机方法) - 等待&lt; 0x22991048&gt; java.lang.Object.wait(Object.java:485)at(java.lang.ref.Reference $ Lock)at at java.lang.ref.Reference中的$ ReferenceHandler.run(Reference.java:116) - 已锁定&lt; 0x22991048&gt; (java.lang.ref.Reference $ Lock)
“main”prio = 6 tid = 0x002a6c00 nid = 0x1fd4等待条件 [0x0090f000] java.lang.Thread.State:TIMED_WAITING(停车)在 sun.misc.Unsafe.park(原生方法) - 停车等待&lt; 0x229c10d8&gt; (一个java.util.concurrent.locks.ReentrantLock $ NonfairSync)at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:196) 在 java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireNanos(AbstractQueuedSynchronizer.java:905) 在 java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireNanos(AbstractQueuedSynchronizer.java:1224) 在 java.util.concurrent.locks.ReentrantLock.tryLock(ReentrantLock.java:416) 在TestLock2.testOne(TestLock2.java:58)at TestLock2.test(TestLock2.java:44)at TestLock2.main(TestLock2.java:10)
“VM Thread”prio = 10 tid = 0x02b0a800 nid = 0x11d0 runnable
“VM Periodic Task Thread”prio = 10 tid = 0x02b65400 nid = 0x1efc等待 条件
JNI全球参考:975
有什么想法吗?
答案 0 :(得分:0)
可能的解释是,在主进程获得系统时间并且线程将开始休眠之后,系统进程调度程序将从执行中删除您的进程。 5秒后,继续执行:线程已经睡了5秒,主线程获得锁定。不过,这有点奇怪。
无论如何,我建议您使用profiler,这样可以让您了解到底发生了什么。