Avatar of m4trix
m4trixFlag for Canada

asked on 

Shared Calendar Object doesn't appear to be thread safe...

Hi there,

I've got what is probably an easy question for some of you.
I've got an application that downloads some data every hour at a certain time. I have a GUI that lists each item with a timer counting down until that task is run, at which point when it completes the timer resets.

The way I have it implemented is I use a single shared Class (Data) that is updated like so:
Timer timer = new Timer(true);
Calendar now = Calendar.getInstance();
timer.scheduleAtFixedRate(new TimerLoop(), 1000-now.get(Calendar.MILLISECOND), 1000);

private class TimerLoop extends TimerTask {
	public void run() {
		Data.now = Calendar.getInstance();
		Data.now.add(Calendar.MILLISECOND, -Data.now.get(Calendar.MILLISECOND));
		curTimeLabel.setText(sdf.format(Data.now.getTime()));
		Data.curMin = Data.now.get(Calendar.MINUTE);
		Data.curSec = Data.now.get(Calendar.SECOND);
	}
}

Open in new window


Then, in each download thread, I have the following:
Timer timer = new Timer(true);
Calendar now = Calendar.getInstance();
timer.scheduleAtFixedRate(new DownloadLoop(), 1500-now.get(Calendar.MILLISECOND), 1000);

protected void updateLabels() {
	nextLabel.setText(timeLeftFormat.format(new Date(nextTry.getTimeInMillis() - Data.now.getTimeInMillis() + 5 * 60 * 60 * 1000)));
}

private class DownloadLoop extends TimerTask {
	public void run() {
		updateLabels();
	}
}

Open in new window


whereby "nextLabel" is simply a JLabel to display the countdown and "nextTry" is the time at which the next download is to occur.

This works great with one download thread. The problem is that when I add more download threads, they occasionally show the same value, even if "nextTry" is different in each instance. I could understand this being a problem if any of the download threads tried to UPDATE Data.now, but as far as I know they should only be accessing it. I can't for the life of me figure out why the label would ever show the same number between two download threads if nextTry is different. Also, this doesn't happen every second, sometimes they show correctly, sometimes not (seemingly random).

Any thoughts? Better ways to achieve the same result?  I tried keeping a separate Calendar object in each download thread, but I found it resulted in the labels being updated at slightly different milliseconds which looked a little bit weird (the way I have it now has them all updating at the same time).

Thanks!
Java

Avatar of undefined
Last Comment
m4trix
ASKER CERTIFIED SOLUTION
Avatar of CPColin
CPColin
Flag of United States of America image

Blurred text
THIS SOLUTION IS ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
Avatar of m4trix
m4trix
Flag of Canada image

ASKER

Ah! That might be it!, The SimpleDateFormat is static, because I assumed it could quite happily shared between the threads (as I assumed it was essentially "read-only"). I will try that tomorrow and let you know
SOLUTION
Avatar of gordon_vt02
gordon_vt02

Blurred text
THIS SOLUTION IS ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
Avatar of m4trix
m4trix
Flag of Canada image

ASKER

Removed the static keyword from the SimpleDateFormat declaration and all SEEMS to be working properly...

Awarded 50 points to gordon simply because I appreciate his suggestion of alternative methods to use that may be better than what I'm doing already
Avatar of gordon_vt02
gordon_vt02

As an aside, whether or not you go with the ScheduledThreadPoolExecutor, you should use the SwingUtilities.invokeLater() method as all GUI updates should be done on the event dispatch thread -- even though it is possible not to.
Avatar of m4trix
m4trix
Flag of Canada image

ASKER

I will give that a go, thanks!
Avatar of m4trix
m4trix
Flag of Canada image

ASKER

Gordon, just so I'm clear, I would use the invokeLater() method like so:

For example, instead of:
protected void updateLabels() {
    label.setText("whatever");
}

Open in new window


I would do:
protected void updateLabels() {
    LabelUpdater temp = new LabelUpdater();
}

Open in new window


and just include the private class definition (for LabelUpdater) as you described above? Is it that simple? Isn't there added overhead instantiating a new object every update, once per thread?
Avatar of gordon_vt02
gordon_vt02

Yes, you would just create a new LabelUpdater to send that message.  You wouldn't even really need to save the reference to it, since there isn't much you can do with the object after it's been created.  There is very minimal overhead, you shouldn't notice it at all.  You could also write a static helper method to take care of the updates, but you'll still be creating a new Runnable for each update in that method -- reusing the same object could cause thread-safety issues and unpredictable results.
Avatar of m4trix
m4trix
Flag of Canada image

ASKER

wonderful, thanks again for your suggestions
Java
Java

Java is a platform-independent, object-oriented programming language and run-time environment, designed to have as few implementation dependencies as possible such that developers can write one set of code across all platforms using libraries. Most devices will not run Java natively, and require a run-time component to be installed in order to execute a Java program.

102K
Questions
--
Followers
--
Top Experts
Get a personalized solution from industry experts
Ask the experts
Read over 600 more reviews

TRUSTED BY

IBM logoIntel logoMicrosoft logoUbisoft logoSAP logo
Qualcomm logoCitrix Systems logoWorkday logoErnst & Young logo
High performer badgeUsers love us badge
LinkedIn logoFacebook logoX logoInstagram logoTikTok logoYouTube logo