원인: 멀티 스레드 환경에서 여러 스레드가 동일한 SimpleDateFormat 개체(예: 정적 수정)를 동시에 사용하는 경우(예: format 메서드 호출) 여러 스레드가 동시에 Calendar.setTime 메서드를 호출합니다. , 다른 스레드에서 시간을 사용하게 되므로 스레드로부터 안전하지 않도록 수정되었습니다.
이 튜토리얼의 운영 환경: 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!