任何人都可以给我一个显示 SimpleDateFormat 线程不安全的例子吗?
Can anyone give me an example of showing SimpleDateFormat is thread unsafe?
我已经尝试编写一个示例来说明 SimpleDateFormat 是线程不安全的。但它不起作用!任何人都可以给我一个显示 SimpleDateFormat 线程不安全的例子吗?
public static void main(String[] args) throws ParseException, InterruptedException {
Date aDate = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-12-15 23:59:59"));
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 1000);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
DataFormatter callable = new DataFormatter(sdf, aDate);
Collection<DataFormatter> callables = Collections.nCopies(1000, callable);
executor.invokeAll(callables);
executor.shutdown();
}
private static class DataFormatter implements Callable<String> {
private SimpleDateFormat sdf;
private Date aDate;
public DataFormatter(SimpleDateFormat sdf, Date aDate) {
this.sdf = sdf;
this.aDate = aDate;
}
@Override
public String call() throws Exception {
String format = sdf.format(aDate);
Assert.assertEquals("2016-12-15 23:59:59", format);
return format;
}
}
Can anyone give me an example of showing SimpleDateFormat is thread unsafe?
当然可以。您的代码的问题在于您试图一遍又一遍地格式化 相同的日期 ,因此共享字段永远不会持有不同的值。如果我们查看 SimpleDateFormat
中的代码,我们会发现它扩展了 DateFormat
,它有一个共享的 Calendar calendar
字段。那是 class.
的重入问题
// shared with everyone else calling the same SimpleDateFormat
protected Calendar calendar;
...
// method from DateFormat that is extended by SimpleDateFormat
private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) {
calendar.setTime(date);
...
顺便说一句,还要注意它使用了 StringBuffer
这意味着它使用了 synchronized
方法。令人沮丧的是,我们为同步性能损失付出了代价,但我们没有通过 SimpleDateFormat
.
重新进入
这是我对如何演示它的看法。我只是在随机日期 运行 两次日期格式并检查结果。只有 20 个并发线程时它会立即失败。
public class SimpleDateFormatEnosafe {
private static final SimpleDateFormat format =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
DataFormatter formatter = new DataFormatter();
for (int i = 0; i < 20; i++) {
executor.submit(formatter);
}
executor.shutdown();
// NOTE: this could never finish if all but one thread fails in the pool
}
private static class DataFormatter implements Runnable {
@Override
public void run() {
ThreadLocalRandom random = ThreadLocalRandom.current();
while (true) {
Date date = new Date(random.nextLong());
String output1 = format.format(date);
String output2 = format.format(date);
if (!output1.equals(output2)) {
System.out.println(output1 + " != " + output2);
break;
}
}
}
}
}
我试过了,结果很简单。我有两个线程使用共享 SimpleDateFormat
实例格式化不同的日期(声明为 DateFormat
,但实际实例是 class SimpleDateFormat
)。这是我的代码:
public class FormattingThread extends Thread {
private final static DateFormat shared = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, Locale.GERMANY);
private final static ZoneOffset offset = ZoneOffset.ofHours(5);
LocalDateTime date;
public FormattingThread(LocalDateTime date) {
super("FormattingThread");
this.date = date;
}
@Override
public void run() {
final DateFormat myOwn = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, Locale.GERMANY);
int stepCount = 0;
while (true) {
stepCount++;
Date classical = Date.from(date.toInstant(offset));
String formatted = myOwn.format(classical);
String formattedThreadUnsafe = shared.format(classical);
if (! formatted.equals(formattedThreadUnsafe)) {
System.err.println("Mine " + formatted + "; shared " + formattedThreadUnsafe
+ " in " + stepCount + " steps");
}
date = date.plusDays(23);
}
}
public static void main(String[] args) {
new FormattingThread(LocalDateTime.now()).start();
new FormattingThread(LocalDateTime.now().plusHours(17)).start();
}
}
在我的计算机上,其中一个线程在第一次调用 format()
时得到了不正确的结果。在每个线程调用数百次后,ArrayIndexOutOfBoundsException
出现在 Calendar
class 中(SimpleDateFormat
使用 Calendar
)。
我仍然想强调,正如已经说过的那样:class 不是线程安全的,不能保证在多个线程中使用它时会发生错误。没有在全面测试中发现自己然后在生产中浮出水面的同步错误的故事很多。我最近在 Java 程序中有一个在 Mac 和 Linux 上没有发生,但是 Windows 用户报告了它,我们不得不快速发布错误修复版本.此类事件的代价相当昂贵。
我已经尝试编写一个示例来说明 SimpleDateFormat 是线程不安全的。但它不起作用!任何人都可以给我一个显示 SimpleDateFormat 线程不安全的例子吗?
public static void main(String[] args) throws ParseException, InterruptedException {
Date aDate = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-12-15 23:59:59"));
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 1000);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
DataFormatter callable = new DataFormatter(sdf, aDate);
Collection<DataFormatter> callables = Collections.nCopies(1000, callable);
executor.invokeAll(callables);
executor.shutdown();
}
private static class DataFormatter implements Callable<String> {
private SimpleDateFormat sdf;
private Date aDate;
public DataFormatter(SimpleDateFormat sdf, Date aDate) {
this.sdf = sdf;
this.aDate = aDate;
}
@Override
public String call() throws Exception {
String format = sdf.format(aDate);
Assert.assertEquals("2016-12-15 23:59:59", format);
return format;
}
}
Can anyone give me an example of showing SimpleDateFormat is thread unsafe?
当然可以。您的代码的问题在于您试图一遍又一遍地格式化 相同的日期 ,因此共享字段永远不会持有不同的值。如果我们查看 SimpleDateFormat
中的代码,我们会发现它扩展了 DateFormat
,它有一个共享的 Calendar calendar
字段。那是 class.
// shared with everyone else calling the same SimpleDateFormat
protected Calendar calendar;
...
// method from DateFormat that is extended by SimpleDateFormat
private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) {
calendar.setTime(date);
...
顺便说一句,还要注意它使用了 StringBuffer
这意味着它使用了 synchronized
方法。令人沮丧的是,我们为同步性能损失付出了代价,但我们没有通过 SimpleDateFormat
.
这是我对如何演示它的看法。我只是在随机日期 运行 两次日期格式并检查结果。只有 20 个并发线程时它会立即失败。
public class SimpleDateFormatEnosafe {
private static final SimpleDateFormat format =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
DataFormatter formatter = new DataFormatter();
for (int i = 0; i < 20; i++) {
executor.submit(formatter);
}
executor.shutdown();
// NOTE: this could never finish if all but one thread fails in the pool
}
private static class DataFormatter implements Runnable {
@Override
public void run() {
ThreadLocalRandom random = ThreadLocalRandom.current();
while (true) {
Date date = new Date(random.nextLong());
String output1 = format.format(date);
String output2 = format.format(date);
if (!output1.equals(output2)) {
System.out.println(output1 + " != " + output2);
break;
}
}
}
}
}
我试过了,结果很简单。我有两个线程使用共享 SimpleDateFormat
实例格式化不同的日期(声明为 DateFormat
,但实际实例是 class SimpleDateFormat
)。这是我的代码:
public class FormattingThread extends Thread {
private final static DateFormat shared = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, Locale.GERMANY);
private final static ZoneOffset offset = ZoneOffset.ofHours(5);
LocalDateTime date;
public FormattingThread(LocalDateTime date) {
super("FormattingThread");
this.date = date;
}
@Override
public void run() {
final DateFormat myOwn = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, Locale.GERMANY);
int stepCount = 0;
while (true) {
stepCount++;
Date classical = Date.from(date.toInstant(offset));
String formatted = myOwn.format(classical);
String formattedThreadUnsafe = shared.format(classical);
if (! formatted.equals(formattedThreadUnsafe)) {
System.err.println("Mine " + formatted + "; shared " + formattedThreadUnsafe
+ " in " + stepCount + " steps");
}
date = date.plusDays(23);
}
}
public static void main(String[] args) {
new FormattingThread(LocalDateTime.now()).start();
new FormattingThread(LocalDateTime.now().plusHours(17)).start();
}
}
在我的计算机上,其中一个线程在第一次调用 format()
时得到了不正确的结果。在每个线程调用数百次后,ArrayIndexOutOfBoundsException
出现在 Calendar
class 中(SimpleDateFormat
使用 Calendar
)。
我仍然想强调,正如已经说过的那样:class 不是线程安全的,不能保证在多个线程中使用它时会发生错误。没有在全面测试中发现自己然后在生产中浮出水面的同步错误的故事很多。我最近在 Java 程序中有一个在 Mac 和 Linux 上没有发生,但是 Windows 用户报告了它,我们不得不快速发布错误修复版本.此类事件的代价相当昂贵。