aspose file tools*
The moose likes Features new in Java 7 and the fly likes Broken Swing Timer in Java 7 Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Spring in Action this week in the Spring forum!
JavaRanch » Java Forums » Java » Features new in Java 7
Bookmark "Broken Swing Timer in Java 7" Watch "Broken Swing Timer in Java 7" New topic
Author

Broken Swing Timer in Java 7

Luigi Plinge
Ranch Hand

Joined: Jan 06, 2011
Posts: 441

The accuracy of Swing's Timer has gone out of the window. Here's a demo:
Tested on Window 7 64-bit.

Java 6 r22 :
Enter delay in ms: 50
Expecting 20.0 events per second
20 events in 1.01 s
20 events in 1.0 s
20 events in 1.0 s

Enter delay in ms: 20
Expecting 50.0 events per second
50 events in 1.0 s
50 events in 1.0 s
50 events in 1.0 s

Enter delay in ms: 10
Expecting 100.0 events per second
99 events in 1.0 s
100 events in 1.0 s
100 events in 1.0 s

Java 7:
Enter delay in ms: 50
Expecting 20.0 events per second
16 events in 1.01 s
17 events in 1.01 s
17 events in 1.02 s

Enter delay in ms: 20
Expecting 50.0 events per second
33 events in 1.02 s
34 events in 1.01 s
34 events in 1.02 s

Enter delay in ms: 10
Expecting 100.0 events per second
48 events in 1.0 s
50 events in 1.0 s
50 events in 1.0 s


Let's just hope no-one's tempted to use Java 7 in any production systems...

[Edit: changed subject to be less confrontational]
Darryl Burke
Bartender

Joined: May 03, 2008
Posts: 4642
    
    5

Were both tests run on the same computer? or at least the same OS and identical hardware?


luck, db
There are no new questions, but there may be new answers.
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19720
    
  20

I wouldn't blame just Java 7. I just tried your code with Java 6u26 on Windows 7 64 bit, and with twice a 50ms delay I got the following results:

Enter delay in ms: 50
Expecting 20.0 events per second
11 events in 1.0 s
17 events in 1.06 s
17 events in 1.06 s
17 events in 1.06 s
17 events in 1.038 s
17 events in 1.06 s


Enter delay in ms: 50
Expecting 20.0 events per second
20 events in 1.002 s
20 events in 1.0 s
20 events in 1.0 s
20 events in 1.0 s
20 events in 1.0 s
20 events in 1.0 s


As you can see there's a 3 event difference with the same hardware, same software, same program. There are a lot of external influences (e.g. other processes running) that can influence the results.


SCJP 1.4 - SCJP 6 - SCWCD 5 - OCEEJBD 6
How To Ask Questions How To Answer Questions
Luigi Plinge
Ranch Hand

Joined: Jan 06, 2011
Posts: 441

It could possibly be something introduced in a late edition of Java 6, since I only have u22. But it is a repeatable bug, which is independent on external influences. If I switch back and forth between the Java 6_22 and 7 libraries, the wrong timing only happens with 7. (It doesn't make a difference which version is used to compile it.)
Walter Gabrielsen Iii
Ranch Hand

Joined: Apr 09, 2011
Posts: 158
Timer runs a thread which might see a cached value, this is a concurrency issue. use volitile keyword on primatives especally long(64-bit can be made of2 32-bit segments), and/or synchronised blocks around compound action0s (delay, count++).
Walter Gabrielsen Iii
Ranch Hand

Joined: Apr 09, 2011
Posts: 158
I typed on a misaligned touch screen the other day, and I wasn't able to be more descriptive then, so here I go again:

I read in, Goetz. Java Concurrency in Practice, that 64-bit value types, double and long, can be represented by two 32-bit values on some systems. It could be the case that only half of your long is being updated, I can imagine that this can especially be a problem on multi-core cpu systems. Therefore, you shouldn't assume that just because you have a 64-bit system that your JavaVM is optimized for it.

To add to the problem, a new Thread creates a new stack of its own and can, if it tries to access a value outside of itself, such as your call to time1 = System.currentTimeMillis(), see a stale, old value, or in the case of split 64-bit data types half of an old value and half an update. This throws the time worthiness out the window.

Also "count++" is actually two compound actions, first read the value then update it, that can result in a "missing update" bug that sets your count backwards, the Thread may fall asleep after it has read count but before completing the increment.

I think you should try to get some concurrency safeguards in place, such as atomic variables, or volatile on long, or synchronized blocks around compound actions in your thread, and try again.
Walter Gabrielsen Iii
Ranch Hand

Joined: Apr 09, 2011
Posts: 158
Okay, you might want to skip the thread counts all together. Just check out Java VisualVM, if you're using Windows look in your Java folder/bin/jvisualvm.exe. It actually keeps track of how many threads are running in each Java program you're running.
Luigi Plinge
Ranch Hand

Joined: Jan 06, 2011
Posts: 441

Hey Walter, all I know is that it used to work just fine, they changed something, and now it doesn't! A quick comparison of the source codes reveals quite a few changes in the Timer class. I don't think the fault is with my little test example. It's a very simple and typical use-case.
Walter Gabrielsen Iii
Ranch Hand

Joined: Apr 09, 2011
Posts: 158
Curious, I looked at the src code of Java 6 and 7.

The first thing I notice is that they've replaced a boolean with an AtomicBoolean:

6

7


Since this AtomicBoolean reference is marked final (I assume this means it will always point to the same object), and, I noticed, AtomicBoolean src uses an immutable storage field, I conclude the notify field in Timer will always be false -- the class can't reassign a true AtomicBoolean to that reference field. It will affect the event/listener model, but I don't know to what effect this is having on performance issue you brought up.

I'm continuing to look through the source code...
Walter Gabrielsen Iii
Ranch Hand

Joined: Apr 09, 2011
Posts: 158
Okay, I don't like where this is going. Private fields probably don't need this level of thread safety when they are only used for an Object's internal computations. And, some of these fields are changing from mutable into immutable references, when they weren't originally. This will be trouble if they didn't alter the programming logic.

Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19720
    
  20

Walter Gabrielsen Iii wrote:Since this AtomicBoolean reference is marked final (I assume this means it will always point to the same object), and, I noticed, AtomicBoolean src uses an immutable storage field, I conclude the notify field in Timer will always be false -- the class can't reassign a true AtomicBoolean to that reference field.

AtomicBoolean has methods to change its value.

As for these changed fields, I'd say that making them volatile or final is an improvement for thread safety. Both ensure that a thread will always use the most up-to-date value for the field, instead of a possibly old cached value.

Walter Gabrielsen Iii wrote:Private fields probably don't need this level of thread safety when they are only used for an Object's internal computations.

The private fields are just as much part of the internal state of an object as non-private fields. To ensure this internal state is valid synchronization techniques are often required, also for private fields. Using volatile and final are two such techniques.
Walter Gabrielsen Iii
Ranch Hand

Joined: Apr 09, 2011
Posts: 158
But, aren't the Timer's internal calculations being run on the same stack/heap area as the Timer object? That is to say there aren't threads running inside Timer to set its state, the fields are encapsulated, and the only access to them is from method calls.
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19720
    
  20

And can you tell with 100% certainty that there is only one thread calling these methods? Hint: package default class javax.swing.TimerQueue which is used by Timer implements Runnable. And it has a method startIfNeeded with the following code:
This method is called from the TimerQueue constructor which is called from static method TimerQueue.sharedInstance() which is called from Timer's timerQueue method which is used all over the place.

So I'll answer my own question: no, there's probably not one thread calling these methods. There are at least two: the TimerQueue thread and the Event Dispatcher Thread.
Darryl Burke
Bartender

Joined: May 03, 2008
Posts: 4642
    
    5

That's also documented in the API for javax.swing.Timer.
Although all Timers perform their waiting using a single, shared thread (created by the first Timer object that executes), the action event handlers for Timers execute on another thread -- the event-dispatching thread.

Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19720
    
  20

Pffff, who reads the API if you can go through code?
Walter Gabrielsen Iii
Ranch Hand

Joined: Apr 09, 2011
Posts: 158
I followed Timer's "this" reference into TimerQueue. The developers also heavily remade that class using the java.util.concurrent.* packages. I can see why thread safety is important.

I just wonder if they over-did the protections. If that is having a performance hit (having to lock and rebuild Timer's "this" reference, which doesn't really leave the TimerQueue until after its repackaged as a subclass thread, finished its delay time, and then fires its ActionEvent). It seems like a lot of trouble to go through just to fire an ActionEvent (I think it just jumped right to start(), using synchronized methods , in Java 6). Did they just go crazy about removing as much non-concurrent package thread-safety-techniques as they could?
Walter Gabrielsen Iii
Ranch Hand

Joined: Apr 09, 2011
Posts: 158
I have an idea. What if the new thread safety of Timer is there because they removed the synchronized methods of TimerQueue? Wouldn't that also mean the new Event method now needs to be synchronized as well?
 
Don't get me started about those stupid light bulbs.
 
subject: Broken Swing Timer in Java 7