Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 谁能给我举个例子说明SimpleDataFormat是线程不安全的?_Java_Multithreading_Thread Safety_Simpledateformat - Fatal编程技术网

Java 谁能给我举个例子说明SimpleDataFormat是线程不安全的?

Java 谁能给我举个例子说明SimpleDataFormat是线程不安全的?,java,multithreading,thread-safety,simpledateformat,Java,Multithreading,Thread Safety,Simpledateformat,我试图写一个例子来说明SimpleDataFormat是线程不安全的。但它不起作用!谁能给我举个例子说明SimpleDataFormat是线程不安全的 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

我试图写一个例子来说明SimpleDataFormat是线程不安全的。但它不起作用!谁能给我举个例子说明SimpleDataFormat是线程不安全的

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;
    }
}
publicstaticvoidmain(String[]args)抛出ParseException、interruptedeexception{
日期日期=(新简化格式(“yyyy-MM-dd HH:MM:ss”).parse(“2016-12-15 23:59:59”);
ExecutorService executor=Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*1000);
SimpleDataFormat sdf=新的SimpleDataFormat(“yyyy-MM-dd HH:MM:ss”);
DataFormatter callable=新的DataFormatter(sdf,aDate);
Collection callables=Collections.nCopies(1000,可调用);
执行人。调用所有(可调用);
executor.shutdown();
}
私有静态类DataFormatter实现可调用{
私人简化格式sdf;
私人约会;
公共数据格式化程序(SimpleDataFormat sdf,日期自适应){
这个.sdf=sdf;
this.aDate=aDate;
}
@凌驾
公共字符串调用()引发异常{
字符串格式=sdf.format(aDate);
Assert.assertEquals(“2016-12-15 23:59:59”,格式);
返回格式;
}
}
谁能给我举个例子说明SimpleDataFormat是线程不安全的

当然。代码的问题在于,您试图一次又一次地格式化同一日期,以便共享字段永远不会包含不同的值。如果我们查看
SimpleDateFormat
中的代码,我们会看到它扩展了
DateFormat
,它有一个共享的
日历
字段。这就是类的重入问题

// 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;
                }
            }
        }
    }
}
公共类SimpleDataFormatOnSafe{
专用静态最终SimpleDataFormat格式=
新的简化格式(“yyyy-MM-dd HH:MM:ss”);
公共静态void main(字符串[]args){
ExecutorService executor=Executors.newCachedThreadPool();
DataFormatter formatter=新的DataFormatter();
对于(int i=0;i<20;i++){
执行人提交(格式化程序);
}
executor.shutdown();
//注意:如果池中除一个线程外的所有线程都失败,则此操作永远不会完成
}
私有静态类DataFormatter实现可运行{
@凌驾
公开募捐{
ThreadLocalRandom=ThreadLocalRandom.current();
while(true){
日期日期=新日期(random.nextLong());
String output1=format.format(日期);
字符串output2=format.format(日期);
如果(!output1.equals(output2)){
System.out.println(output1+“!=”+output2);
打破
}
}
}
}
}
谁能给我举个例子说明SimpleDataFormat是线程不安全的

当然。代码的问题在于,您试图一次又一次地格式化同一日期,以便共享字段永远不会包含不同的值。如果我们查看
SimpleDateFormat
中的代码,我们会看到它扩展了
DateFormat
,它有一个共享的
日历
字段。这就是类的重入问题

// 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;
                }
            }
        }
    }
}
公共类SimpleDataFormatOnSafe{
专用静态最终SimpleDataFormat格式=
新的简化格式(“yyyy-MM-dd HH:MM:ss”);
公共静态void main(字符串[]args){
ExecutorService executor=Executors.newCachedThreadPool();
DataFormatter formatter=新的DataFormatter();
对于(int i=0;i<20;i++){
执行人提交(格式化程序);
}
executor.shutdown();
//注意:如果池中除一个线程外的所有线程都失败,则此操作永远不会完成
}
私有静态类DataFormatter实现可运行{
@凌驾
公开募捐{
ThreadLocalRandom=ThreadLocalRandom.current();
while(true){
日期日期=新日期(random.nextLong());
String output1=format.format(日期);
字符串output2=format.format(日期);
如果(!output1.equals(output2)){
System.out.println(output1+“!=”+output2);
打破
}
}
}
}
}

我试过了,结果很简单。我有两个线程使用共享的
SimpleDateFormat
实例格式化不同的日期(声明的
DateFormat
,但实际实例属于类
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()
,结果不正确。从每个线程调用几百次后,
Calendar
类中出现
ArrayIndexOutOfBoundsException
SimpleDateFormat
使用
Calendar

我仍然想强调,正如已经说过的那样:类不是线程安全的,不能保证在mor中使用它时会发生错误