>  기사  >  Java  >  simpledateformat 스레드가 왜 안전하지 않습니까?

simpledateformat 스레드가 왜 안전하지 않습니까?

青灯夜游
青灯夜游원래의
2021-05-10 14:40:2417227검색

원인: 멀티 스레드 환경에서 여러 스레드가 동일한 SimpleDateFormat 개체(예: 정적 수정)를 동시에 사용하는 경우(예: format 메서드 호출) 여러 스레드가 동시에 Calendar.setTime 메서드를 호출합니다. , 다른 스레드에서 시간을 사용하게 되므로 스레드로부터 안전하지 않도록 수정되었습니다.

simpledateformat 스레드가 왜 안전하지 않습니까?

이 튜토리얼의 운영 환경: windows7 시스템, java8 버전, DELL G3 컴퓨터.

스레드 안전하지 않은 확인:

/**
 * SimpleDateFormat线程安全测试
 * 〈功能详细描述〉
 *
 * @author 17090889
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class SimpleDateFormatTest {
    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"));

    public void test() {
        while (true) {
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    String dateString = simpleDateFormat.format(new Date());
                    try {
                        Date parseDate = simpleDateFormat.parse(dateString);
                        String dateString2 = simpleDateFormat.format(parseDate);
                        System.out.println(dateString.equals(dateString2));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

출력:

  true
  false
  true
  true
  false

false가 나타나 스레드가 안전하지 않음을 나타냅니다.

1. 형식 방법

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);

        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;
    }
 protected Calendar calendar;

캘린더 변수가 여러 스레드 간에 공유되고 캘린더가 수정되었습니다. 따라서 멀티 스레드 환경에서 여러 스레드가 동일한 SimpleDateFormat 개체(예: 정적 수정)를 동시에 사용하는 경우(예: format 메서드 호출) 여러 스레드가 동시에 calender.setTime 메서드를 호출하게 되어 따라서 스레드는 안전하지 않습니다.

또한 구문 분석 메서드는 스레드에 안전하지 않습니다. 구문 분석 메서드는 실제로 구문 분석을 위해 CalenderBuilder의 설정을 호출합니다.

해결책:

1. SimpleDateFormat을 로컬 변수로 정의

2. 스레드 동기화 잠금 추가: 동기화(lock)

3. ThreadLocal을 사용하면 각 스레드는 SimpleDateFormat 개체의 자체 복사본을 갖습니다.

/**
 * SimpleDateFormat线程安全测试
 * 〈功能详细描述〉
 *
 * @author 17090889
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class SimpleDateFormatTest {
        private static final ThreadLocal<SimpleDateFormat> THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };
    //    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"));

    public void test() {
        while (true) {
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    SimpleDateFormat simpleDateFormat = THREAD_LOCAL.get();
                    if (simpleDateFormat == null) {
                        simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    }
                    String dateString = simpleDateFormat.format(new Date());
                    try {
                        Date parseDate = simpleDateFormat.parse(dateString);
                        String dateString2 = simpleDateFormat.format(parseDate);
                        System.out.println(dateString.equals(dateString2));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    } finally {
                        local.remove();
                    }
                }
            });
        }
    }
}

4. SimpleDateFormat 대신 DateTimeFormatter를 사용하세요.

DateTimeFormatter는 스레드로부터 안전하며 기본적으로 ofPattern 메서드를 통해 다양한 형식 지정 방법을 생성할 수도 있습니다.

  (1) 날짜 형식 예:

 LocalDateTime localDateTime = LocalDateTime.now();
 System.out.println(localDateTime); // 2019-11-20T15:04:29.017
 DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
 String strDate=localDateTime.format(dtf);
 System.out.println(strDate); // 2019/23/20 15:23:46

  (2) 날짜 분석

 DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
 LocalDateTime localDateTime=LocalDateTime.parse("2019/11/20 15:23:46",dtf);
 System.out.println(localDateTime); // 2019-11-20T15:23:46

추천 관련 비디오 튜토리얼: Java 비디오 튜토리얼

위 내용은 simpledateformat 스레드가 왜 안전하지 않습니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.