-2

I tried to create a static method that is accessed by Multiple methods of different classes at a time.

As it is static, its gets locked and I am getting different results and performance problems.

Is there a better approach to make these methods accessible to multiple methods at a time?

Is there a better way to handle this in Spring Framework?

  public class TestUtil {

  public static SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");

   public static java.sql.Date getLastDayOfMonth(String month,intyear) 
    throws ParseException{      
    switch (month) {
    case "jan": 
             return new java.sql.Date(f.parse(year+"-1-31").getTime());
    case "feb": if(isLeapYear(year))
             return new java.sql.Date(f.parse(year+"-2-29").getTime());
             else
              return new java.sql.Date(f.parse(year+"-2-28").getTime());
    case "mar":                
             return new java.sql.Date(f.parse(year+"-3-31").getTime());

    case "apr": 
             return new java.sql.Date(f.parse(year+"-4-30").getTime());
    case "may": 
             return new java.sql.Date(f.parse(year+"-5-31").getTime());
    case "jun": 
             return new java.sql.Date(f.parse(year+"-6-30").getTime());
    case "jul": 
             return new java.sql.Date(f.parse(year+"-7-31").getTime());
    case "aug": 
             return new java.sql.Date(f.parse(year+"-8-31").getTime());
    case "sep": 
             return new java.sql.Date(f.parse(year+"-9-30").getTime());
    case "oct": 
             return new java.sql.Date(f.parse(year+"-10-31").getTime());
    case "nov": 
             return new java.sql.Date(f.parse(year+"-11-30").getTime());
    case "dec": 
             return new java.sql.Date(f.parse(year+"-12-31").getTime());        
    default: month = "Invalid month";
    return null;  
}           
}
}
ryanyuyu
  • 6,366
  • 10
  • 48
  • 53
Cork Kochi
  • 1,783
  • 6
  • 29
  • 45
  • 3
    What makes you think multithreading is the performance bottleneck here? And btw, SimpleDateFormat [isn't thread safe](https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html): "Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally." – yshavit Jan 13 '16 at 21:05

3 Answers3

1

Editorial

Your technique is terrible.

Answer

Inject an instantiated object into your class using Spring. The default type is singleton, which is what you want. Don't store any state in your utility method. Use a non-static method.

Don't parse String to find the date, instead use the Calendar class. Also, you can just as easily not have a static SimpleDateFormat and just use a local. Here is some code (which uses a static SimpleDateFormat).

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class Dates
{
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

    private static Date getLastDay(final int month, final int year)
    {
        final Calendar calendar = Calendar.getInstance();
        Date returnValue;

        switch (month)
        {
            case Calendar.DECEMBER:
                calendar.set(Calendar.YEAR, year + 1);
                calendar.set(Calendar.MONTH, Calendar.JANUARY);
                calendar.set(Calendar.DAY_OF_MONTH, 1);
                calendar.add(Calendar.DAY_OF_YEAR, -1);

                returnValue = calendar.getTime();

                break;

            case Calendar.JANUARY:
                calendar.set(Calendar.YEAR, year);
                calendar.set(Calendar.MONTH, Calendar.FEBRUARY);
                calendar.set(Calendar.DAY_OF_MONTH, 1);
                calendar.add(Calendar.DAY_OF_YEAR, -1);

                returnValue = calendar.getTime();
                break;

            case Calendar.FEBRUARY:
                calendar.set(Calendar.YEAR, year);
                calendar.set(Calendar.MONTH, Calendar.MARCH);
                calendar.set(Calendar.DAY_OF_MONTH, 1);
                calendar.add(Calendar.DAY_OF_YEAR, -1);

                returnValue = calendar.getTime();
                break;

            default:
                returnValue = new Date();
                break;
        }

        return returnValue;
    }

    public static void main(
        String[] args)
    {
        Date date;

        System.out.println("Dec 2015: " + dateFormat.format(getLastDay(Calendar.DECEMBER, 2015)));
        System.out.println("Jan 2015: " + dateFormat.format(getLastDay(Calendar.JANUARY, 2015)));
        System.out.println("Feb 2015: " + dateFormat.format(getLastDay(Calendar.FEBRUARY, 2015)));
        System.out.println("Feb 2016: " + dateFormat.format(getLastDay(Calendar.FEBRUARY, 2016)));
    }

}
DwB
  • 37,124
  • 11
  • 56
  • 82
  • What if I have to return a java.sql.Date rather than java.util.date – Cork Kochi Jan 13 '16 at 23:13
  • I suggest you read the API docs for the java.sql.Date and java.util.Date classes. java.sql.Date can be constructed with a long milliseconds. java.util.Date has a method (getDate()) that returns the Date value in milliseconds. returnValue = new java.sql.Date(calendar.getTime().getTime()); – DwB Jan 14 '16 at 15:30
  • private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); This is not thread safe right ?http://stackoverflow.com/questions/5652518/confusion-about-thread-safety-simpledateformat-example – Manjush Jan 14 '16 at 22:11
  • When used only to format, i believe that SimpleDateFormat is thread safe. consider reading the answer you referenced, .parse and .format are, believe it or not, different methods. – DwB Jan 15 '16 at 01:18
1

One of the best things you can do for safety is to eliminate state changes by multiple threads, and most certain way to do that is to use immutable objects.

It's often overlooked, but a SimpleDateFormat has mutable state (it tracks parse operation state between calls). So, using it from multiple threads concurrently doesn't work, as you've found.

Look for alternatives to eliminate shared state. In this case, it's easy: just use the improved API for dates and time in Java 8:

private static final DateTimeFormatter f; 

static {
  DateTimeFormatterBuilder b = new DateTimeFormatterBuilder();
  b.parseCaseInsensitive();
  b.parseDefaulting(ChronoField.DAY_OF_MONTH, 1);
  b.appendPattern("uuuuMMM");
  f = b.toFormatter(Locale.US); /* f is an immutable object! */
}

public static LocalDate getLastDayOfMonth(String month, int year)
{
  LocalDate first = LocalDate.parse(year + month, f);
  LocalDate last = first.with(TemporalAdjusters.lastDayOfMonth());
  return last;
}
erickson
  • 265,237
  • 58
  • 395
  • 493
0

If you ABSOLUTELY need a static field change it to ThreadLocal<U>, in your case public static ThreadLocal<SimpleDateFormat> f = new ThreadLocal<SimpleDateFormat>();, or, if not, change the fields to local variables in your static methods and you will be fine.

Radu Ionescu
  • 3,462
  • 5
  • 24
  • 43
  • ThreadLocal objects and local variables are stored on the Stack Memory. If you want a shared state between threads (atomicity) you will need another approach, however. – Radu Ionescu Jan 14 '16 at 07:57