8031701: java/lang/management/ThreadMXBean/Locks.java: Thread WaitingThread is expected to wait on Object but got null Thread.State = RUNNABLE
Reviewed-by: mchung, dsamersoff
This commit is contained in:
parent
da17ca59db
commit
9e782fae6d
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -27,18 +27,19 @@
|
|||||||
* @summary Basic unit test of ThreadInfo.getLockName()
|
* @summary Basic unit test of ThreadInfo.getLockName()
|
||||||
* and ThreadInfo.getLockOwnerName()
|
* and ThreadInfo.getLockOwnerName()
|
||||||
* @author Mandy Chung
|
* @author Mandy Chung
|
||||||
|
* @author Jaroslav Bachorik
|
||||||
*
|
*
|
||||||
* @build ThreadExecutionSynchronizer
|
|
||||||
* @run main/othervm Locks
|
* @run main/othervm Locks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.lang.management.*;
|
import java.lang.management.*;
|
||||||
|
import java.util.concurrent.Phaser;
|
||||||
|
|
||||||
public class Locks {
|
public class Locks {
|
||||||
private static Object objA = new Object();
|
private static final Object objA = new Object();
|
||||||
private static Object objB = new Object();
|
private static final Object objB = new Object();
|
||||||
private static Object objC = new Object();
|
private static final Object objC = new Object();
|
||||||
private static ThreadMXBean tm = ManagementFactory.getThreadMXBean();
|
private static final ThreadMXBean tm = ManagementFactory.getThreadMXBean();
|
||||||
|
|
||||||
private static boolean testFailed = false;
|
private static boolean testFailed = false;
|
||||||
|
|
||||||
@ -46,48 +47,62 @@ public class Locks {
|
|||||||
if (lock == null) return null;
|
if (lock == null) return null;
|
||||||
|
|
||||||
return lock.getClass().getName() + '@' +
|
return lock.getClass().getName() + '@' +
|
||||||
Integer.toHexString(System.identityHashCode(lock));
|
Integer.toHexString(System.identityHashCode(lock));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertNoLock(Thread t) {
|
||||||
|
long tid = t.getId();
|
||||||
|
ThreadInfo info = tm.getThreadInfo(tid);
|
||||||
|
String result = info.getLockName();
|
||||||
|
|
||||||
|
if (result != null) {
|
||||||
|
throw new RuntimeException("Thread " + t.getName() + " is not supposed to hold any lock. " +
|
||||||
|
"Currently owning lock: " + result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkBlockedObject(Thread t, Object lock, Thread owner,
|
private static void checkBlockedObject(Thread t, Object lock, Thread owner,
|
||||||
Thread.State expectedState) {
|
Thread.State expectedState) {
|
||||||
ThreadInfo info = tm.getThreadInfo(t.getId());
|
long tid = t.getId();
|
||||||
|
ThreadInfo info = tm.getThreadInfo(tid);
|
||||||
String result = info.getLockName();
|
String result = info.getLockName();
|
||||||
String expectedLock = (lock != null ? getLockName(lock) : null);
|
String expectedLock = (lock != null ? getLockName(lock) : null);
|
||||||
String expectedOwner = (owner != null ? owner.getName() : null);
|
String expectedOwner = (owner != null ? owner.getName() : null);
|
||||||
|
|
||||||
if (lock != null) {
|
if (lock != null) {
|
||||||
if (expectedState ==Thread.State.BLOCKED) {
|
if (expectedState == Thread.State.BLOCKED) {
|
||||||
int retryCount=0;
|
int retryCount=0;
|
||||||
while(info.getThreadState() != Thread.State.BLOCKED) {
|
while(info.getThreadState() != Thread.State.BLOCKED) {
|
||||||
if (retryCount++ > 500) {
|
if (retryCount++ > 500) {
|
||||||
throw new RuntimeException("Thread " + t.getName() +
|
throw new RuntimeException("Thread " + t.getName() +
|
||||||
" is expected to block on " + expectedLock +
|
" is expected to block on " + expectedLock +
|
||||||
" but got " + result +
|
" but got " + result +
|
||||||
" Thread.State = " + info.getThreadState());
|
" Thread.State = " + info.getThreadState());
|
||||||
}
|
}
|
||||||
goSleep(100);
|
goSleep(100);
|
||||||
|
info = tm.getThreadInfo(tid);
|
||||||
|
result = info.getLockName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (expectedState == Thread.State.WAITING &&
|
if (expectedState == Thread.State.WAITING &&
|
||||||
info.getThreadState() != Thread.State.WAITING) {
|
info.getThreadState() != Thread.State.WAITING) {
|
||||||
throw new RuntimeException("Thread " + t.getName() +
|
throw new RuntimeException("Thread " + t.getName() +
|
||||||
" is expected to wait on " + expectedLock +
|
" is expected to wait on " + expectedLock +
|
||||||
" but got " + result +
|
" but got " + result +
|
||||||
" Thread.State = " + info.getThreadState());
|
" Thread.State = " + info.getThreadState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((result != null && !result.equals(expectedLock)) ||
|
if ((result != null && !result.equals(expectedLock)) ||
|
||||||
(result == null && expectedLock != null)) {
|
(result == null && expectedLock != null)) {
|
||||||
throw new RuntimeException("Thread " + t.getName() + " is blocked on " +
|
throw new RuntimeException("Thread " + t.getName() + " is blocked on " +
|
||||||
expectedLock + " but got " + result);
|
expectedLock + " but got " + result);
|
||||||
}
|
}
|
||||||
result = info.getLockOwnerName();
|
result = info.getLockOwnerName();
|
||||||
if ((result != null && !result.equals(expectedOwner)) ||
|
if ((result != null && !result.equals(expectedOwner)) ||
|
||||||
(result == null && expectedOwner != null)) {
|
(result == null && expectedOwner != null)) {
|
||||||
throw new RuntimeException("Owner of " + lock + " should be " +
|
throw new RuntimeException("Owner of " + lock + " should be " +
|
||||||
expectedOwner + " but got " + result);
|
expectedOwner + " but got " + result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,53 +115,49 @@ public class Locks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ThreadExecutionSynchronizer thrsync = new ThreadExecutionSynchronizer();
|
private static volatile int dummyCounter = 0;
|
||||||
static ThreadExecutionSynchronizer thrsync1 = new ThreadExecutionSynchronizer();
|
|
||||||
|
|
||||||
static class LockAThread extends Thread {
|
static class LockAThread extends Thread {
|
||||||
public LockAThread() {
|
private final Phaser p;
|
||||||
|
public LockAThread(Phaser p) {
|
||||||
super("LockAThread");
|
super("LockAThread");
|
||||||
|
this.p = p;
|
||||||
}
|
}
|
||||||
public void run() {
|
public void run() {
|
||||||
synchronized(objA) {
|
synchronized(objA) {
|
||||||
// stop here for LockBThread to hold objB
|
// stop here for LockBThread to hold objB
|
||||||
thrsync.waitForSignal();
|
System.out.println("LockAThread about to block on objB");
|
||||||
|
p.arriveAndAwaitAdvance(); // Phase 1 (blocking)
|
||||||
System.out.println("LockAThread about to block on objB");
|
synchronized(objB) {
|
||||||
synchronized(objB) {};
|
dummyCounter++;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
p.arriveAndAwaitAdvance(); // Phase 2 (blocking)
|
||||||
System.out.println("LockAThread about to exit");
|
System.out.println("LockAThread about to exit");
|
||||||
// The state could be anything. The expected state value
|
// Make sure the current thread is not holding any lock
|
||||||
// passed with this method is not verified.
|
assertNoLock(this);
|
||||||
checkBlockedObject(this, null, null, Thread.State.TERMINATED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class LockBThread extends Thread {
|
static class LockBThread extends Thread {
|
||||||
public LockBThread() {
|
private final Phaser p;
|
||||||
|
public LockBThread(Phaser p) {
|
||||||
super("LockBThread");
|
super("LockBThread");
|
||||||
|
this.p = p;
|
||||||
}
|
}
|
||||||
public void run() {
|
public void run() {
|
||||||
synchronized(objB) {
|
synchronized(objB) {
|
||||||
// signal waiting LockAThread.
|
System.out.println("LockBThread about to block on objC");
|
||||||
thrsync.signal();
|
p.arriveAndAwaitAdvance(); // Phase 1 (blocking)
|
||||||
|
// Signal main thread about to block on objC
|
||||||
System.out.println("LockBThread about to block on objC");
|
synchronized(objC) {
|
||||||
// Signal main thread about to block on objC
|
dummyCounter++;
|
||||||
thrsync1.signal();
|
};
|
||||||
synchronized(objC) {};
|
|
||||||
}
|
}
|
||||||
|
p.arriveAndAwaitAdvance(); // Phase 2 (blocking)
|
||||||
System.out.println("LockBThread about to exit");
|
System.out.println("LockBThread about to exit");
|
||||||
// The state could be anything. The expected state value
|
// Make sure the current thread is not holding any lock
|
||||||
// passed with this method is not verified.
|
assertNoLock(this);
|
||||||
checkBlockedObject(this, null, null, Thread.State.TERMINATED);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void aboutToLockC() {
|
|
||||||
// Stop here till LockBThread about to blocked
|
|
||||||
// for lock objC.
|
|
||||||
thrsync1.waitForSignal();
|
|
||||||
goSleep(500);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,32 +165,36 @@ public class Locks {
|
|||||||
private static Object ready = new Object();
|
private static Object ready = new Object();
|
||||||
private static CheckerThread checker;
|
private static CheckerThread checker;
|
||||||
static class WaitingThread extends Thread {
|
static class WaitingThread extends Thread {
|
||||||
public WaitingThread() {
|
private final Phaser p;
|
||||||
|
public WaitingThread(Phaser p) {
|
||||||
super("WaitingThread");
|
super("WaitingThread");
|
||||||
|
this.p = p;
|
||||||
}
|
}
|
||||||
public void run() {
|
public void run() {
|
||||||
synchronized(objC) {
|
synchronized(objC) {
|
||||||
System.out.println("WaitingThread about to wait on objC");
|
System.out.println("WaitingThread about to wait on objC");
|
||||||
try {
|
try {
|
||||||
// Signal checker thread, about to wait on objC.
|
// Signal checker thread, about to wait on objC.
|
||||||
thrsync.signal();
|
p.arriveAndAwaitAdvance(); // Phase 1 (waiting)
|
||||||
objC.wait();
|
objC.wait();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
testFailed = true;
|
testFailed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// block until CheckerThread finishes checking
|
// block until CheckerThread finishes checking
|
||||||
System.out.println("WaitingThread about to block on ready");
|
System.out.println("WaitingThread about to block on ready");
|
||||||
// signal checker thread that it is about acquire
|
// signal checker thread that it is about acquire
|
||||||
// object ready.
|
// object ready.
|
||||||
thrsync.signal();
|
p.arriveAndAwaitAdvance(); // Phase 2 (waiting)
|
||||||
synchronized(ready) {};
|
synchronized(ready) {
|
||||||
|
dummyCounter++;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
synchronized(objC) {
|
synchronized(objC) {
|
||||||
try {
|
try {
|
||||||
// signal checker thread, about to wait on objC
|
// signal checker thread, about to wait on objC
|
||||||
thrsync.signal();
|
p.arriveAndAwaitAdvance(); // Phase 3 (waiting)
|
||||||
objC.wait();
|
objC.wait();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -190,21 +205,23 @@ public class Locks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
static class CheckerThread extends Thread {
|
static class CheckerThread extends Thread {
|
||||||
public CheckerThread() {
|
private final Phaser p;
|
||||||
|
public CheckerThread(Phaser p) {
|
||||||
super("CheckerThread");
|
super("CheckerThread");
|
||||||
|
this.p = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForState(Thread.State state) {
|
private void waitForState(Thread.State state) {
|
||||||
thrsync.waitForSignal();
|
p.arriveAndAwaitAdvance();
|
||||||
while (waiter.getState() != state) {
|
while (!waiter.isInterrupted() && waiter.getState() != state) {
|
||||||
goSleep(10);
|
goSleep(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
synchronized (ready) {
|
synchronized (ready) {
|
||||||
// wait until WaitingThread about to wait for objC
|
// wait until WaitingThread about to wait for objC
|
||||||
waitForState(Thread.State.WAITING);
|
waitForState(Thread.State.WAITING); // Phase 1 (waiting)
|
||||||
checkBlockedObject(waiter, objC, null, Thread.State.WAITING);
|
checkBlockedObject(waiter, objC, null, Thread.State.WAITING);
|
||||||
|
|
||||||
synchronized (objC) {
|
synchronized (objC) {
|
||||||
@ -213,13 +230,13 @@ public class Locks {
|
|||||||
|
|
||||||
// wait for waiter thread to about to enter
|
// wait for waiter thread to about to enter
|
||||||
// synchronized object ready.
|
// synchronized object ready.
|
||||||
waitForState(Thread.State.BLOCKED);
|
waitForState(Thread.State.BLOCKED); // Phase 2 (waiting)
|
||||||
checkBlockedObject(waiter, ready, this, Thread.State.BLOCKED);
|
checkBlockedObject(waiter, ready, this, Thread.State.BLOCKED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for signal from waiting thread that it is about
|
// wait for signal from waiting thread that it is about
|
||||||
// wait for objC.
|
// wait for objC.
|
||||||
waitForState(Thread.State.WAITING);
|
waitForState(Thread.State.WAITING); // Phase 3 (waiting)
|
||||||
synchronized(objC) {
|
synchronized(objC) {
|
||||||
checkBlockedObject(waiter, objC, Thread.currentThread(), Thread.State.WAITING);
|
checkBlockedObject(waiter, objC, Thread.currentThread(), Thread.State.WAITING);
|
||||||
objC.notify();
|
objC.notify();
|
||||||
@ -235,24 +252,24 @@ public class Locks {
|
|||||||
LockAThread t1;
|
LockAThread t1;
|
||||||
LockBThread t2;
|
LockBThread t2;
|
||||||
|
|
||||||
|
Phaser p = new Phaser(3);
|
||||||
synchronized(objC) {
|
synchronized(objC) {
|
||||||
// The state could be anything. The expected state value
|
// Make sure the main thread is not holding any lock
|
||||||
// passed with this method is not verified.
|
assertNoLock(mainThread);
|
||||||
checkBlockedObject(mainThread, null, null, Thread.State.RUNNABLE);
|
|
||||||
|
|
||||||
// Test deadlock case
|
// Test deadlock case
|
||||||
// t1 holds lockA and attempts to lock B
|
// t1 holds lockA and attempts to lock B
|
||||||
// t2 holds lockB and attempts to lock C
|
// t2 holds lockB and attempts to lock C
|
||||||
t1 = new LockAThread();
|
|
||||||
|
t1 = new LockAThread(p);
|
||||||
t1.start();
|
t1.start();
|
||||||
|
|
||||||
t2 = new LockBThread();
|
t2 = new LockBThread(p);
|
||||||
t2.start();
|
t2.start();
|
||||||
|
|
||||||
t2.aboutToLockC();
|
p.arriveAndAwaitAdvance(); // Phase 1 (blocking)
|
||||||
|
|
||||||
checkBlockedObject(t1, objB, t2, Thread.State.BLOCKED);
|
|
||||||
checkBlockedObject(t2, objC, mainThread, Thread.State.BLOCKED);
|
checkBlockedObject(t2, objC, mainThread, Thread.State.BLOCKED);
|
||||||
|
checkBlockedObject(t1, objB, t2, Thread.State.BLOCKED);
|
||||||
|
|
||||||
long[] expectedThreads = new long[3];
|
long[] expectedThreads = new long[3];
|
||||||
expectedThreads[0] = t1.getId(); // blocked on lockB
|
expectedThreads[0] = t1.getId(); // blocked on lockB
|
||||||
@ -260,13 +277,14 @@ public class Locks {
|
|||||||
expectedThreads[2] = mainThread.getId(); // owner of lockC
|
expectedThreads[2] = mainThread.getId(); // owner of lockC
|
||||||
findThreadsBlockedOn(objB, expectedThreads);
|
findThreadsBlockedOn(objB, expectedThreads);
|
||||||
}
|
}
|
||||||
goSleep(100);
|
p.arriveAndAwaitAdvance(); // Phase 2 (blocking)
|
||||||
|
|
||||||
|
p = new Phaser(2);
|
||||||
// Test Object.wait() case
|
// Test Object.wait() case
|
||||||
waiter = new WaitingThread();
|
waiter = new WaitingThread(p);
|
||||||
waiter.start();
|
waiter.start();
|
||||||
|
|
||||||
checker = new CheckerThread();
|
checker = new CheckerThread(p);
|
||||||
checker.start();
|
checker.start();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -284,7 +302,7 @@ public class Locks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ThreadInfo findOwnerInfo(ThreadInfo[] infos, String lock)
|
private static ThreadInfo findOwnerInfo(ThreadInfo[] infos, String lock)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
ThreadInfo ownerInfo = null;
|
ThreadInfo ownerInfo = null;
|
||||||
for (int i = 0; i < infos.length; i++) {
|
for (int i = 0; i < infos.length; i++) {
|
||||||
String blockedLock = infos[i].getLockName();
|
String blockedLock = infos[i].getLockName();
|
||||||
@ -292,7 +310,7 @@ public class Locks {
|
|||||||
long threadId = infos[i].getLockOwnerId();
|
long threadId = infos[i].getLockOwnerId();
|
||||||
if (threadId == -1) {
|
if (threadId == -1) {
|
||||||
throw new RuntimeException("TEST FAILED: " +
|
throw new RuntimeException("TEST FAILED: " +
|
||||||
lock + " expected to have owner");
|
lock + " expected to have owner");
|
||||||
}
|
}
|
||||||
for (int j = 0; j < infos.length; j++) {
|
for (int j = 0; j < infos.length; j++) {
|
||||||
if (infos[j].getThreadId() == threadId) {
|
if (infos[j].getThreadId() == threadId) {
|
||||||
@ -305,7 +323,7 @@ public class Locks {
|
|||||||
return ownerInfo;
|
return ownerInfo;
|
||||||
}
|
}
|
||||||
private static void findThreadsBlockedOn(Object o, long[] expectedThreads)
|
private static void findThreadsBlockedOn(Object o, long[] expectedThreads)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String lock = getLockName(o);
|
String lock = getLockName(o);
|
||||||
// Check with ThreadInfo with no stack trace (i.e. no safepoint)
|
// Check with ThreadInfo with no stack trace (i.e. no safepoint)
|
||||||
ThreadInfo[] infos = tm.getThreadInfo(tm.getAllThreadIds());
|
ThreadInfo[] infos = tm.getThreadInfo(tm.getAllThreadIds());
|
||||||
@ -317,14 +335,14 @@ public class Locks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void doCheck(ThreadInfo[] infos, String lock, long[] expectedThreads)
|
private static void doCheck(ThreadInfo[] infos, String lock, long[] expectedThreads)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
ThreadInfo ownerInfo = null;
|
ThreadInfo ownerInfo = null;
|
||||||
// Find the thread who is blocking on lock
|
// Find the thread who is blocking on lock
|
||||||
for (int i = 0; i < infos.length; i++) {
|
for (int i = 0; i < infos.length; i++) {
|
||||||
String blockedLock = infos[i].getLockName();
|
String blockedLock = infos[i].getLockName();
|
||||||
if (lock.equals(blockedLock)) {
|
if (lock.equals(blockedLock)) {
|
||||||
System.out.print(infos[i].getThreadName() +
|
System.out.print(infos[i].getThreadName() +
|
||||||
" blocked on " + blockedLock);
|
" blocked on " + blockedLock);
|
||||||
ownerInfo = infos[i];
|
ownerInfo = infos[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,7 +354,7 @@ public class Locks {
|
|||||||
ownerInfo = findOwnerInfo(infos, lock);
|
ownerInfo = findOwnerInfo(infos, lock);
|
||||||
threads[count++] = ownerInfo.getThreadId();
|
threads[count++] = ownerInfo.getThreadId();
|
||||||
System.out.println(" Owner = " + ownerInfo.getThreadName() +
|
System.out.println(" Owner = " + ownerInfo.getThreadName() +
|
||||||
" id = " + ownerInfo.getThreadId());
|
" id = " + ownerInfo.getThreadId());
|
||||||
lock = ownerInfo.getLockName();
|
lock = ownerInfo.getLockName();
|
||||||
System.out.print(ownerInfo.getThreadName() + " Id = " +
|
System.out.print(ownerInfo.getThreadName() + " Id = " +
|
||||||
ownerInfo.getThreadId() +
|
ownerInfo.getThreadId() +
|
||||||
@ -346,13 +364,13 @@ public class Locks {
|
|||||||
|
|
||||||
if (count != expectedThreads.length) {
|
if (count != expectedThreads.length) {
|
||||||
throw new RuntimeException("TEST FAILED: " +
|
throw new RuntimeException("TEST FAILED: " +
|
||||||
"Expected chain of threads not matched; current count =" + count);
|
"Expected chain of threads not matched; current count =" + count);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
if (threads[i] != expectedThreads[i]) {
|
if (threads[i] != expectedThreads[i]) {
|
||||||
System.out.println("TEST FAILED: " +
|
System.out.println("TEST FAILED: " +
|
||||||
"Unexpected thread in the chain " + threads[i] +
|
"Unexpected thread in the chain " + threads[i] +
|
||||||
" expected to be " + expectedThreads[i]);
|
" expected to be " + expectedThreads[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
||||||
*
|
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License version 2 only, as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* version 2 for more details (a copy is included in the LICENSE file that
|
|
||||||
* accompanied this code).
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License version
|
|
||||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
||||||
* or visit www.oracle.com if you need additional information or have any
|
|
||||||
* questions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* @summary This class is used to synchronize execution of two threads.
|
|
||||||
* @author Swamy Venkataramanappa
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.util.concurrent.Semaphore;
|
|
||||||
|
|
||||||
public class ThreadExecutionSynchronizer {
|
|
||||||
|
|
||||||
private volatile boolean waiting;
|
|
||||||
private final Semaphore semaphore;
|
|
||||||
|
|
||||||
public ThreadExecutionSynchronizer() {
|
|
||||||
semaphore = new Semaphore(1);
|
|
||||||
waiting = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Synchronizes two threads execution points.
|
|
||||||
// Basically any thread could get scheduled to run and
|
|
||||||
// it is not possible to know which thread reaches expected
|
|
||||||
// execution point. So whichever thread reaches a execution
|
|
||||||
// point first wait for the second thread. When the second thread
|
|
||||||
// reaches the expected execution point will wake up
|
|
||||||
// the thread which is waiting here.
|
|
||||||
void stopOrGo() {
|
|
||||||
semaphore.acquireUninterruptibly(); // Thread can get blocked.
|
|
||||||
if (!waiting) {
|
|
||||||
waiting = true;
|
|
||||||
// Wait for second thread to enter this method.
|
|
||||||
while(!semaphore.hasQueuedThreads()) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(20);
|
|
||||||
} catch (InterruptedException xx) {}
|
|
||||||
}
|
|
||||||
semaphore.release();
|
|
||||||
} else {
|
|
||||||
waiting = false;
|
|
||||||
semaphore.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper function just for code readability.
|
|
||||||
void waitForSignal() {
|
|
||||||
stopOrGo();
|
|
||||||
goSleep(50);
|
|
||||||
}
|
|
||||||
|
|
||||||
void signal() {
|
|
||||||
stopOrGo();
|
|
||||||
goSleep(50);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void goSleep(long ms) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(ms);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
System.out.println("Unexpected exception.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user