JDK-6541879 : Thread.sleep() within a synchronized block influences subsequent lock aquisition
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux
  • CPU: x86
  • Submitted: 2007-04-03
  • Updated: 2011-02-16
  • Resolved: 2007-04-03
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
[~]$ java -version
java version "1.6.0_01"
Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
Java HotSpot(TM) Client VM (build 1.6.0_01-b06, mixed mode, sharing)

FULL OS VERSION :
[test]$ uname -a
Linux zinad 2.6.18-4-k7 #1 SMP Mon Mar 26 17:57:15 UTC 2007 i686 GNU/Linux


EXTRA RELEVANT SYSTEM CONFIGURATION :
proc: AthlonXP 2000+


A DESCRIPTION OF THE PROBLEM :
The following code contains two //-------\\ lines between which Thread.sleep() is called.
If that call is commented out, the average for the time reported is 1076 milliseconds.
If uncommented, the average for the time reported is 52301 milliseconds. I also encountered two cases in which the process had to be killed not returning after aproximately 40 min of running.

for comparison, in Windows XP when commented ��� 1025 milliseconds, when uncommented ��� 1074 milliseconds.

The averages were computed from at least 10 results each.

THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: No

THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: No

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
javac MainAndThread1.java
java MainAndThread1

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED: either the Thread.sleep() method called or not the two threads should have equivalent chances of aquiring the lock once the Thread2 object has exited method2().

ACTUAL: if the synchronized block calls Thread.sleep(), the thread that has the lock (Thread2) has more chances of aquiring the lock in the next step.
REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
//*****************************************************\import java.util.Date;
import java.util.concurrent.TimeUnit;

public class MainAndThread1 {
	public static void main(String[] args) {
		LockableObject lo = new LockableObject();
		Date d1 = new Date();
		new Thread(new Thread2(lo)).start();
		try {
			TimeUnit.MILLISECONDS.sleep(1000);
		} catch(InterruptedException ignore) {}
		lo.method1();
		Date d2 = new Date();
		System.out.println("It took: " + (d2.getTime() - d1.getTime()) + " milliseconds");
		System.exit(0);
	}
}

class Thread2 implements Runnable{
	private LockableObject lo;
	public Thread2(LockableObject lo) {
		this.lo = lo;
	}
	public void run() {
		while (true) {
			lo.method2();
		}
	}
}

class LockableObject {
	public synchronized void method1() {
		System.out.println("method1");
	}
	public synchronized void method2() {
		System.out.println("method2");
//-----------------------------------------------------------------------------------\		try {
			TimeUnit.NANOSECONDS.sleep(5);
		} catch(InterruptedException ignore) {}
//-----------------------------------------------------------------------------------\	}
}
//***************************************************************\---------- END SOURCE ----------

Comments
EVALUATION See 4985566 for more information.
03-04-2007

EVALUATION There is no fairness guarantee in lock acquisition. If thread 2 acquires the lock and goes to sleep then thread 1 will block when it tries to acquire the lock. When thread 2 releases the lock thread 1 will be woken, but before thread 1 can get rescheduled on a processor, thread 2 will loop around, reacquire the lock and hence force thread 1 to block again. Eventually thread 2 may lose its timeslice when not holding the lock in which case thread 1 may eventually get its turn to run. Or if on a multiprocessor thread 1 might also be lucky and grab the lock from thread 2. But in principle this kind of looping lock acquisition can lead to starvation. Also note that a sleep time of 5 nanoseconds can not be honoured. In this case Thread.sleep will convert the sleep time to 1 millisecond. But even that can not be honoured on many systems where 10ms is the smallest sleep interval due to the resolution of the system timers.
03-04-2007