Static method in multithreading - java

I have the following code in my class
private static final SimpleDateFormat SDF_ISO_DATE = new SimpleDateFormat("yyyy-MM-dd");
private static final SimpleDateFormat SDF_ISO_TIME = new SimpleDateFormat("HH:mm:ss");
public static String getTimeStampAsString(final long time) {
TimeZone tz = TimeZone.getTimeZone("UTC");
SDF_ISO_DATE.setTimeZone(tz);
SDF_ISO_TIME.setTimeZone(tz);
return SDF_ISO_DATE.format(
new Date(time)) + " " + SDF_ISO_TIME.format(new Date(time)
);
}
In my multi threaded application the following method returns date in future, even for the current date, is the static method or variable is responsible for this?
edit:
I had the following code to reproduce and prove what are mentioned in the answers,but still not able to.Can some one help me for the same.
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<String> task = new Callable<String>(){
public String call() throws Exception {
return DateUtil.getTimeStampAsString(1524567870569L);
}
};
//pool with 50 threads
ExecutorService exec = Executors.newFixedThreadPool(50);
List<Future<String>> results = new ArrayList<Future<String>>();
//perform 10 date conversions
for(int i = 0 ; i < 50 ; i++){
results.add(exec.submit(task));
}
exec.shutdown();
//look at the results
for(Future<String> result : results){
System.out.println(result.get());
}
}

is the static method or variable is responsible for this?
Static variables. SimpleDateFormat isn't thread-safe, which should be obvious since you're modifying its internal state by calling setTimeZone(). It means that several threads could be doing that at the same time, which should feel like producing unpredictable results.
You need to build your formats locally rather than reuse some defined statically. Or better yet, drop Java's old time-managing classes and use java.time.* instead.

As an answer to your edit: how to reproduce the problem with thread-unsafety (not sure whether that really ought to be a separate question). Formatting the same date in two or more threads using the same SimpleDateFormat seems to go well (at least most often, no guarantee that it always will). Try formatting different date-times, and it will be very easy to get wrong results. I changed your task like this:
AtomicLong time = new AtomicLong(1_524_567_870_569L);
Callable<String> task = new Callable<String>(){
#Override
public String call() {
return DateUtil.getTimeStampAsString(time.getAndAdd(2_768_461_000L));
}
};
It’s easiest to see that the results are wrong when I also sort them in the output, so I have done that. I am only quoting the first few results from one run since this is enough to demonstrate the problem:
2018-04-24 11:04:30
2018-05-26 12:05:31
2018-06-11 13:06:32
2018-07-29 14:07:33
2018-08-08 15:08:34
2018-10-01 16:09:35
…
The expected result was (obtained by declaring getTimeStampAsString() synchronized; also sorted afterward):
2018-04-24 11:04:30
2018-05-26 12:05:31
2018-06-27 13:06:32
2018-07-29 14:07:33
2018-08-30 15:08:34
2018-10-01 16:09:35
…
Already the fifth printed result has the day-of-month all wrong, 08 instead of 30, and there are many more errors in the full list. You may try it yourself. As you probably know, exact results are not reproducible, but you should get results that are wrong somehow.
PS Here’s my code for printing the results in sorted order in case you want to try it:
//look at the results
SortedSet<String> sorted = new TreeSet<>();
for (Future<String> result : results){
sorted.add(result.get());
}
sorted.forEach(System.out::println);

tl;dr
To capture the current moment and generate a string in your desired format (which is a modified form of standard ISO 8601 format), use the java.time classes. These classes are much simpler and vastly better designed. They are also thread-safe.
Instant.now().toString().replace( "T" , " " )
Current moment
Your method is named getCurrentTimeStamp(final Date date) yet you are passing an existing Date object set to a specific moment rather than capturing the current moment.
Nowhere in your code do I see you capturing the current moment. If you want the current moment, call Instant.now() as shown below.
Avoid legacy date-time classes
The legacy date-time classes such as Date & SimpleDateFormat are not thread-safe. One of many reasons to avoid these troublesome classes. They were supplanted years ago by the java.time classes.
java.time
As a moment in UTC, the java.util.Date class is replaced by the Instant class. Same idea, but Instant has a resolution in nanoseconds rather than milliseconds. And Instant::toString does not inject a time zone dynamically as Date::toString does.
To capture the current moment in UTC, call the static Instant.now() method.
Instant instant = Instant.now() ; // Capture current moment in UTC.
Parse your input number as a count of milliseconds since the epoch reference of first moment of 1970 in UTC.
Instant instant = Instant.ofEpochMilli( 1_524_567_870_569L ) ;
instant.toString(): 2018-04-24T11:04:30.569Z
No need for must of your code. No need for your DateUtil, as seen in code above. No need for custom formatting patterns, as your desired format happens to comply with the ISO 8601 standard used by default in the java.time classes. If the T in the middle bothers you or your users, replace with a SPACE.
String output = instant.toString().replace( "T" , " " ) ;
2018-04-24T11:04:30.569Z
ExecutorService blocking
You seem to misunderstand ExecutorService::shutdown. That method does not block to wait for tasks to complete. As your code is written, some tasks may not yet be done running until after you report results (partially-completed results).
Add a call to ExecutorService::awaitTermination, as seen in code below. Set a time-out long enough that if exceeded it must mean some problem occurred. To quote the doc:
Block until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
See example code below. For more discussion see this Question, ExecutorService - How to wait for completition of all tasks in non-blocking style
Threads
The java.time classes are thread-safe by design. They use the immutable objects pattern, returning fresh object based on existing values rather than changing (“mutating”) the original.
Example code. Your Question is confused about whether you want a hard-coded moment or the current moment. Switch to either by enabling the commented-out line in this example.
Callable < String > task = new Callable < String >() {
public String call () throws Exception {
long threadId = Thread.currentThread().getId();
// String moment = Instant.ofEpochMilli( 1524567870569L ).toString().replace( "T" , " " );
String moment = Instant.now().toString().replace( "T" , " " );
String output = ( moment + " | " + threadId );
return output;
}
};
// Pool with 5 threads
ExecutorService exec = Executors.newFixedThreadPool( 5 );
List < Future < String > > results = new ArrayList < Future < String > >();
// Perform a certain number of tasks.
int countAssignedTasks = 500;
for ( int i = 0 ; i < countAssignedTasks ; i++ ) {
results.add( exec.submit( task ) );
}
// Wait for tasks to complete.
Boolean completedBeforeTimeOut = null;
try {
exec.shutdown();
completedBeforeTimeOut = exec.awaitTermination( 5 , TimeUnit.SECONDS ); // Block until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
} catch ( InterruptedException e ) {
e.printStackTrace();
}
// Report results.
System.out.println( "completedBeforeTimeOut: " + completedBeforeTimeOut );
for ( Future < String > result : results ) {
try {
System.out.println( result.get() );
} catch ( InterruptedException e ) {
e.printStackTrace();
} catch ( ExecutionException e ) {
e.printStackTrace();
}
}
System.out.println( "BASIL - done." );
When run.
Note that the times are not chronological. In multi-threaded code, you cannot predict which tasks will be executed when.
2018-04-24 20:24:06.991225Z | 13
2018-04-24 20:24:06.991246Z | 14
2018-04-24 20:24:06.991236Z | 15
2018-04-24 20:24:06.991232Z | 16
2018-04-24 20:24:06.991222Z | 17
2018-04-24 20:24:07.067002Z | 16
2018-04-24 20:24:07.067009Z | 17

tz is effectively constant and the setters don't do anything after the first invocation of either method. Use a static initialiser to set the timezone right away to make the methods thread-safe.
private static final SimpleDateFormat SDF_ISO_DATE = new SimpleDateFormat("yyyy-MM-dd");
private static final SimpleDateFormat SDF_ISO_TIME = new SimpleDateFormat("HH:mm:ss");
static {
TimeZone tz = TimeZone.getTimeZone("UTC");
SDF_ISO_DATE.setTimeZone(tz);
SDF_ISO_TIME.setTimeZone(tz);
}
public static String getCurrentTimeStamp(final Date date) {
return SDF_ISO_DATE.format(date) + " " + SDF_ISO_TIME.format(date);
}
public static String getTimeStampAsString(final long time) {
return getCurrentTimeStamp(new Date(time));
}

Related

Using Duration to check if hours between two days are still within Duration

I was working with LocalDateTime and trying to see if the purchase date(in my case) is withing last x units(days/hours)
I achieved this the following way
public static final int BOOK_PURCHASED_MIN_HOURS = 72;
private boolean isWithinAllowedMinTime(LocalDateTime purchaseDateTime) {
return !LocalDateTime.now(ZoneOffset.UTC).minusHours(BOOK_PURCHASED_MIN_HOURS ).isAfter(purchaseDateTime);
}
This works perfectly fine and gives me true or false if the book has been purchase in 72 hours
I was wondering something like this can be done but with Duration in java where I do not have to worry about time unit and simply can specify like PT03D or PT72H
I was wondering something like this can be done but with Duration in
java where I do not have to worry about time unit and simply can
specify like PT03D or PT72H
Of course, you can do so. You can pass a duration string to your function and parse it to a Duration object to perform a calculation based on it.
I also recommend you use OffsetDateTime instead of LocalDateTime so that you can use the same offset with OffsetDateTime#now.
Demo:
class Main {
public static void main(String[] args) {
// Tests
System.out.println(isWithinAllowedMinTime(OffsetDateTime.now(ZoneOffset.UTC).minusHours(50), "PT72H")); // true
System.out.println(isWithinAllowedMinTime(OffsetDateTime.now(ZoneOffset.UTC).minusHours(75), "PT72H")); // false
System.out.println(isWithinAllowedMinTime(OffsetDateTime.now(ZoneOffset.UTC).minusHours(50), "P3DT12H")); // true
System.out.println(isWithinAllowedMinTime(OffsetDateTime.now(ZoneOffset.UTC).minusHours(72), "P3DT12H")); // true
System.out.println(isWithinAllowedMinTime(OffsetDateTime.now(ZoneOffset.UTC).minusHours(90), "P3DT12H")); // false
}
static boolean isWithinAllowedMinTime(OffsetDateTime purchaseDateTime, String strDuration) {
Duration duration = Duration.parse(strDuration);
return !OffsetDateTime.now(purchaseDateTime.getOffset()).minus(duration).isAfter(purchaseDateTime);
}
}
Output:
true
false
true
true
false
Learn more about the modern Date-Time API from Trail: Date Time.
I wrote my own utility where you can specify time interval as "3d" for 3 days "72h" for 72 hours and so on. This utility is part of Open Source MgntUtils Java library written and maintained by me. Here is a code to demonstrate the point:
private static final String BOOK_PURCHASED_MIN_INTERVAL = "72h"; //possible values could be like "3d" for 3 days
private static void timeIntervalTest() {
TimeInterval bookPurchesedMinInterval = TextUtils.parseStringToTimeInterval(BOOK_PURCHASED_MIN_INTERVAL);
ZonedDateTime purchaseDateTime = ZonedDateTime.now().minusHours(71); //This is to simulate your parameter of book purchacing date
long seconds = ZonedDateTime.now().toEpochSecond() - purchaseDateTime.toEpochSecond();
System.out.println(seconds > bookPurchesedMinInterval.toSeconds());
}
Here is Javadoc for TextUtils.parseStringToTimeInterval() method. If you want to use the library you can get it it as maven artifacts or on Github (source code and Javadoc included). Here is the link for full Javadoc

How to update Date() every second?

I am trying to make a program that updates currentTime every second so that in the console it will go 1s, 2s, 3s, 4s and so on.
public static void main(String[] args) throws InterruptedException{
OSpanel runner = new OSpanel();
runner.currentTime();
}
public static void currentTime() throws InterruptedException{
if(true) {
Date currentTime = new Date();
while(true) {
Thread.sleep(1000);
System.out.println(currentTime);
Thread.sleep(1000);
System.out.println(currentTime);
}
}
}
java.time
The java.util Date-Time API is outdated and error-prone. It is recommended to stop using it completely and switch to the modern Date-Time API*.
You can use Instant#now to get the current instant of time. In order to get it every second, you can use ScheduledExecutorService#scheduleWithFixedDelay e.g.
import java.time.Instant;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) {
Executors.newScheduledThreadPool(1)
.scheduleWithFixedDelay(
() -> System.out.println(Instant.now()),
0,
1,
TimeUnit.SECONDS
);
}
}
Output from a sample run:
2021-10-03T13:53:42.462768Z
2021-10-03T13:53:43.469758Z
2021-10-03T13:53:44.470316Z
...
ONLINE DEMO
An Instant represents an instantaneous point on the timeline, normally represented in UTC time. The Z in the output is the timezone designator for a zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).
Note: If you want to print just the running second, replace the print statement with the following:
System.out.println(ZonedDateTime.now(ZoneOffset.UTC).getSecond() + "s")
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project. Android 8.0 Oreo already provides support for java.time.
You are updating currentTime outside of the while loop - you are outputting the date to the console every second but you are not updating the time.
Try this:
Main.java
public static void main(String[] args) throws InterruptedException{
OSpanel runner = new OSpanel();
runner.currentTime();
}
OSpanel.java
public void currentTime() throws InterruptedException{
int counter = 1;
while (true) {
System.out.println(counter + "s");
Thread.sleep(1000);
counter++;
}
}
}
Duration
Adding to the good Answer by Avinash, let me show the use of Duration to track elapsed time.
Record a starting moment.
Instant start = Instant.now() ; // Capture the current moment as seen in UTC.
At any other moment, calculate elapsed time on a scale of hours-minutes-seconds using the java.time.Duration class.
Duration d = Duration.between( start , Instant.now() ) ;
Generate text representing that elapsed time in standard ISO 8601 format: PnYnMnDTnHnMnS .
String output = Duration.toString() ;
PT21s
To generate text in a custom format, write a method that accesses the various parts. Call toHoursPart, toMinutesPart, etc. Put those parts together in whatever way you desire.
Pulling this all together, in the code of that other Answer, change this line:
() -> System.out.println( Instant.now() ) ,
… to this line:
() -> System.out.println( Duration.between( start , Instant.now() ).toString() ) ,
… or call your custom formatting method.
This will create a thread pool with one thread which will execute the lambda printing the number of seconds since the program started once every second.
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class SOAnswer {
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
long start = System.currentTimeMillis();
executorService.scheduleAtFixedRate(() -> System.out.println(String.format("%ds", (System.currentTimeMillis() - start) / 1000)), 0, 1, TimeUnit.SECONDS);
}
}

Java execute method using a Date object

Right now my program accepts an input, and formats it into a Date. But I want it to call a method whenever that date is reached. How could I do this without the use of any libraries like Quartz?
Code I have for the input:
Date date = new Date();
String inputDate;
month = (String) comboBoxMonth.getSelectedItem();
day = Integer.parseInt((String) comboBoxDay.getSelectedItem());
hours = Integer.parseInt((String) comboBoxTimeH.getSelectedItem());
minutes = Integer.parseInt((String) comboBoxTimeM.getSelectedItem());
try {
//Month/Day/Year Hour:minute:second
inputDate = month + "/" + day + "/" + year + " " + hours + ":" + minutes;
date = formatter.parse(inputDate);
} catch (ParseException e) {
e.printStackTrace();
}
You can use Timer and TimerTask object.
Timer timer = new Timer ();
TimerTask myTask = new TimerTask () {
#Override
public void run () {
// call your method here
}
};
// Schedule the task. Start it when your date is reached!
timer.schedule(myTask, yourDate);
Timer object allow you to handle multiple TimerTask instance!
After the line where you parse the date, add t.schedule(task, date), where 't' is a Timer, and 'task' is a TimerTask that represents the method you want to be executed at the given date.
The Timer class mentioned in another Answer is the old way.
Executor
As of Java 5, the modern way is the Executors suite of interfaces and classes, specifically the ScheduledExecutorService.
Be sure to read up, including searching StackOverflow for more info. Specifically, be aware that any uncaught exception bubbling up to your main code running in the Executor will cause service to cease. Any future scheduled runs of your code will be terminated. The solution is simple: Always surround the main code of your executor with a try-catch to catch any Exception (and maybe even Error, or, Throwable).
Never Use Timer In Servlet/JaveEE
Most especially, do not use Timer in a Servlet or Java EE (Enterprise Edition) app. See this Answer by BalusC for details.

Time difference calculation issues

I have a table called by name Symbols in my Application which will be updated continously for every 8 minutes
Each record inside the Symbol table has got a attribute by name updated-at and whose value is in timestamp as shown
"updated_at" : NumberLong("1375715967249")
I have a task to show the updated data to the users from the symbols table
In case the symbol is not updated for 9 minutes , i need to executed a particular task and if updated a different task
I was following this logic , please let me know if this has got any loop holes ?? ( I mean like day like settings --- or any such )
package com;
public class UnixTimeConversion {
public static void main(String args[]) {
long timeStamp = 1375715967249l;
java.util.Date date = new java.util.Date();
long currtime = date.getTime();
if ((currtime - timeStamp) > 600000) {
System.out.println("Greater than 10 minutes since executed");
} else {
System.out.println("Lesser than 10 minutes since executed");
}
}
}
Better to try in this way
long timeStamp = 1375715967249l;
long currTime = System.currentTimeMillis();
if ((currTime - timeStamp) > 10*60*1000) {
System.out.println("Greater than 10 minutes since executed");
} else {
System.out.println("Lesser than 10 minutes since executed");
}
10min = 10*60*1000 ms
UNIX timestamps don't care about Timezones, UTC leap seconds or anything. It's just a number linearly measuring the passing of time. If you don't care about wallclock time either, there's no problem. You just have to take care that you convert your source material to UNIX timestamps in the right manner.

Will be a bug of SimpleDateFormat.parse

I have the following code:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TimeZone tz2 = TimeZone.getTimeZone("GMT");
dateFormat.setTimeZone(tz2);
String aDate = "2012-05-02 23:59:59";
for(int i=0 ; i<1000; i++){
dateFormat.setLenient(false);
ParsePosition p = new ParsePosition(0);
Date date = dateFormat.parse(aDate, p);
java.sql.Date sqlDate = null;
if (p.getIndex() != aDate.length())
throw new RuntimeException("just a test");
}
After testing plenty of times, it was very stranger. Basically, it never be finished completely, it ran into exception very randomly.
You see the code should be correct, but: it ran into exception when maybe i is 500 or i is 799 or i is 988(just take some examples here, means it was not getting happened when i = 0, it actually has finished some circles), the exception may get thrown in either line Date date = dateFormat.parse(aDate, p); or line throw new RuntimeException("just a test");;
Can everybody advice me whats wrong?
SimpleDateFormat.parse() uses an instance variable called calendar to build the date from the string. If two threads try to parse at the same time, the calendar variable will get clobbered and you'll get wrong results.
Making the variable not static won't necessarily help, since two threads could still be using the same controller. A better solution is to either create a new DateFormat object each time you parse a date, or use thread local storage. Better still, use JodaTime which has thread safe parsers.
Also consider the following points while using SDF.
Creating SimpleDateFormat is expensive. Don't use this unless it's done seldom.
OK if you can live with a bit of blocking. Use if formatDate() is not used much.
Fastest option IF you reuse threads (thread pool). Uses more memory than 2. and has higher startup overhead.
For applications both 2. and 3. are viable options. Which is best for your case depends on your use case. Beware of premature optimization. Only do it if you believe this is an issue.
For libraries that would be used by 3rd party I'd use option 3.
Your code just plain works, even after 20 reruns. So let's take a guess at what your real SSCCE might look like:
public static void main(String[] args) {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
dateFormat.setLenient(false);
final String aDate = "2012-05-02 23:59:59";
for (int i = 0; i < 2; i++)
new Thread() { public void run() {
for (int i = 0; i < 1000; i++) {
ParsePosition p = new ParsePosition(0);
dateFormat.parse(aDate, p);
if (p.getIndex() != aDate.length())
throw new RuntimeException("just a test");
}
System.out.println("Done");
}}.start();
}
This breaks, more or less the way you describe, and for obvious reasons. Listen to #Bhavik Ambani's advice, he's covering this for you.
p.getIndex() != aDate.length()
This statement is returning true, which means that when you parsed the date you did not consume the entirety of string aDate, mean aDate either has timezone or other information also

Categories

Resources