1. 程式人生 > 其它 >SimpleDateFormat執行緒不安全

SimpleDateFormat執行緒不安全

技術標籤:java後端

測試程式碼:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class SimpleDateFormatTest {

    //這裡只建立了一個物件(該物件擁有共享的成員變數protected Calendar calendar;)
    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000), new MyThreadFactory("SimpleDateFormatTest"));
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000));

    public void test() {
    	System.out.println("================================1");
    	int i = 0;
        while (true) {
        	i++;
        	System.out.println("start................");
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                	System.out.println("run................");
                    String dateString = simpleDateFormat.format(new Date());
                    try {
                        Date parseDate = simpleDateFormat.parse(dateString);
                        String dateString2 = simpleDateFormat.format(parseDate);
                        System.out.println(dateString.equals(dateString2));
                        if(!dateString.equals(dateString2)){
                        	return ;
                        }
                        System.out.println("runEnd................");
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
            });
            
            System.out.println("end................");
            if(i==20){
            	return;
            }
        }
        
    }
    
    public static void main(String[] args) {
    	SimpleDateFormatTest sdft = new SimpleDateFormatTest();
    	sdft.test();
	}
    
}

輸出:

  true
  false
  true
  true
  false

出現了false,說明執行緒不安全。

public class TestdateString {

private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

public static void main(String[] args) throws ParseException {
String dateString = simpleDateFormat.format(new Date());

Date parseDate = simpleDateFormat.parse(dateString);
String dateString2 = simpleDateFormat.format(parseDate);
System.out.println(dateString.equals(dateString2));

}

}

# 擷取部分SimpleDateFormat 程式碼

# 擷取SimpleDateFormat  部分程式碼分析執行緒不安全問題


public class SimpleDateFormat extends DateFormat {

   ...........................
   ................................
  
 @Override
    public StringBuffer format(Date date, StringBuffer toAppendTo,
                               FieldPosition pos)
    {
        pos.beginIndex = pos.endIndex = 0;
        return format(date, toAppendTo, pos.getFieldDelegate());
    }

    // Called from Format after creating a FieldDelegate
    private StringBuffer format(Date date, StringBuffer toAppendTo,
                                FieldDelegate delegate) {
        // Convert input date to time field list
        calendar.setTime(date);
//解析,這裡的calendar  來自父類DateFormat 的成員變數calendar ,(只單例建立了一個物件????,被所有執行緒共享??? 所以有執行緒安全問題。)

        boolean useDateFormatSymbols = useDateFormatSymbols();

        for (int i = 0; i < compiledPattern.length; ) {
            int tag = compiledPattern[i] >>> 8;
            int count = compiledPattern[i++] & 0xff;
            if (count == 255) {
                count = compiledPattern[i++] << 16;
                count |= compiledPattern[i++];
            }

            switch (tag) {
            case TAG_QUOTE_ASCII_CHAR:
                toAppendTo.append((char)count);
                break;

            case TAG_QUOTE_CHARS:
                toAppendTo.append(compiledPattern, i, count);
                i += count;
                break;

            default:
                subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
                break;
            }
        }
        return toAppendTo;
    }

....................


}

擷取DateFormat 類部分程式碼:

package java.text;

public abstract class DateFormat extends Format {

protected Calendar calendar;


protected NumberFormat numberFormat;

 public final static int ERA_FIELD = 0;


public final StringBuffer format(Object obj, StringBuffer toAppendTo,
                                     FieldPosition fieldPosition)
    {
        if (obj instanceof Date)
            return format( (Date)obj, toAppendTo, fieldPosition );
        else if (obj instanceof Number)
            return format( new Date(((Number)obj).longValue()),
                          toAppendTo, fieldPosition );
        else
            throw new IllegalArgumentException("Cannot format given Object as a Date");
    }


//抽象方法
 public abstract StringBuffer format(Date date, StringBuffer toAppendTo,
                                        FieldPosition fieldPosition);


 public final String format(Date date)
    {
        return format(date, new StringBuffer(),
                      DontCareFieldPosition.INSTANCE).toString();
    }
........................

}

Calendar類

public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {

................

    @SuppressWarnings("ProtectedField")
    protected long          time;
    @SuppressWarnings("ProtectedField")
    protected boolean       isTimeSet;
    @SuppressWarnings("ProtectedField")
    protected boolean       areFieldsSet;
    transient boolean       areAllFieldsSet;


 public final void setTime(Date date) {
        setTimeInMillis(date.getTime());
    }


public void setTimeInMillis(long millis) {
        // If we don't need to recalculate the calendar field values,
        // do nothing.
        if (time == millis && isTimeSet && areFieldsSet && areAllFieldsSet
            && (zone instanceof ZoneInfo) && !((ZoneInfo)zone).isDirty()) {
            return;
        }
        time = millis;
        isTimeSet = true;
        areFieldsSet = false;
        computeFields();
        areAllFieldsSet = areFieldsSet = true;
    }

................

}