SimpleDateFormat is not thread safe in Java

Objective:

  1. To prove SimpleDateFormat is not thread safe in Java
  2. To make SimpleDateFormat thread-safe

Proof:

import java.text.ParseException;
import java.text.SimpleDateFormat;

public class SDFNotThreadsafeDemo {

	private final static SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy");

	private static String[] stringDates = { "21-12-2012", "10-10-2013", "23-02-2014" };

	public static void main(String[] args) {

		Thread t1 = new Thread(new MyThread());
		Thread t2 = new Thread(new MyThread());
		Thread t3 = new Thread(new MyThread());
		t1.start();
		t2.start();
		t3.start();
	}

	private static class MyThread implements Runnable {

		@Override
		public void run() {
			for (String strDate : stringDates) {
				try {
					System.out.println(sdf1.parse(strDate));
				} catch (ParseException e) {
					e.printStackTrace();
				}
			}
		}

	}
}

 

Following code proves that SimpleDateFormat in Java is not thread-safe.

Here 3 threads are using the same instance of SimpleDateFormat for parsing the strings into Dates. When all the 3 threads use the same instance of SimpleDateFormat, we get either incorrect dates or NumberFormatException.

Exception in thread “Thread-1” Exception in thread “Thread-2” java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1101)
at java.lang.Double.parseDouble(Double.java:540)
at java.text.DigitList.getDouble(DigitList.java:168)
at java.text.DecimalFormat.parse(DecimalFormat.java:1321)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2088)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455)
at java.text.DateFormat.parse(DateFormat.java:355)
at SDFNotThreadsafeDemo$MyThread.run(SDFNotThreadsafeDemo.java:26)
at java.lang.Thread.run(Thread.java:724)

Sat Dec 22 00:00:00 IST 2012
Thu Oct 10 00:00:00 IST 2013
Sun Feb 23 00:00:00 IST 2014

 

In result, you can see NumberFormatException and also an incorrect date. We have 21-12-2012, but result shows 22 Dec 2012.

 

How to make SimpleDateFormat thread-safe?

  1. Make SimpleDateFormat a local variable.(Causes performance slow down)
    This way, every call to  thread will have its own local variable.

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    
    public class SDFThreadsafeDemo {
    
    	private static String[] stringDates = { "21-12-2012", "10-10-2013", "23-02-2014" };
    
    	public static void main(String[] args) {
    
    		Thread t1 = new Thread(new MyThread());
    		Thread t2 = new Thread(new MyThread());
    		Thread t3 = new Thread(new MyThread());
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    
    	private static class MyThread implements Runnable {
    
    		@Override
    		public void run() {
    			for (String strDate : stringDates) {
    				try {
    					SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy");
    					System.out.println(sdf1.parse(strDate));
    				} catch (ParseException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    
    	}
    
    }

    Above approach solves the thread-safety problem of SimpleDateFormat but, creating SimpleDateFormat is a costly operation. If you are looking for performance, then this approach is not good.

  2. Use SimpleDateFormat instance in synchronized block.
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    
    public class SDFThreadsafeDemo {
    
    	private static String[] stringDates = { "21-12-2012", "10-10-2013", "23-02-2014" };
    	private static final SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy");
    	
    	public static void main(String[] args) {
    
    		Thread t1 = new Thread(new MyThread());
    		Thread t2 = new Thread(new MyThread());
    		Thread t3 = new Thread(new MyThread());
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    
    	private static class MyThread implements Runnable {
    
    		@Override
    		public void run() {
    			for (String strDate : stringDates) {
    				try {
    					synchronized (sdf1) {
    						System.out.println(sdf1.parse(strDate));
    					}
    				} catch (ParseException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    }
    

     

    This method
    – makes SimpleDateFormat thread-safe.
    – Uses only one object of SimpleDateFormat for parsing
    – But, every time parsing happens, the instance of SimpleDateFormat is blocked (synchronized block).

     

  3. Use ThreadLocal
    ThreadLocal is a Java class which make instance variable of a class as local to a thread.

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    
    public class SDFThreadsafeDemo {
    
    	private final static ThreadLocal threadLocal = new ThreadLocal() {
    		protected Object initialValue() {
    			return new SimpleDateFormat("dd-MM-yyyy");
    		};
    	};
    	
    	private static String[] stringDates = { "21-12-2012", "10-10-2013", "23-02-2014" };
    
    	public static void main(String[] args) {
    
    		Thread t1 = new Thread(new MyThread());
    		Thread t2 = new Thread(new MyThread());
    		Thread t3 = new Thread(new MyThread());
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    
    	private static class MyThread implements Runnable {
    
    		@Override
    		public void run() {
    			for (String strDate : stringDates) {
    				try {
    					SimpleDateFormat sdf = threadLocal.get();
    					System.out.println(sdf.parse(strDate));
    				} catch (ParseException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    
    	}
    
    }

Leave a Reply