Java最佳实践–多线程环境中的DateFormat

这是有关使用Java编程语言时的拟议实践的系列文章的第一篇。

所有讨论的主题均基于用例,这些用例来自于电信行业的关键任务超高性能生产系统的开发。

在阅读本文的每个部分之前,强烈建议您参考相关的Java API文档以获取详细信息和代码示例。

所有测试均针对具有以下特征的Sony Vaio进行:

  • 系统:openSUSE 11.1(x86_64)
  • 处理器(CPU):Intel(R)Core(TM)2 Duo CPU T6670 @ 2.20GHz
  • 处理器速度:1,200.00 MHz
  • 总内存(RAM):2.8 GB
  • Java:OpenJDK 1.6.0_0 64位

应用以下测试配置:

  • 并发工作者线程:200
  • 每个工作人员重复测试的次数:1000
  • 整体测试次数:100

在多线程环境中使用DateFormat

在多线程环境中使用DateFormat可能很棘手。 Java API文档明确指出:

日期格式不同步。 建议为每个线程创建单独的格式实例。 如果多个线程同时访问一种格式,则必须在外部进行同步。

典型的情况是使用预定义格式将日期转换为字符串表示形式,反之亦然。 为每次转换创建新的DateFormat实例效率很低。 您应该记住,静态工厂方法“ getDateInstance(..)”在使用时也会创建新的DateFormat实例。 大多数开发人员所做的就是使用DateFormat实现类(例如SimpleDateFormat )构造DateFormat实例,并将其值分配给类变量。 类范围的变量用于其所有日期解析和格式设置需求。 前面提到的方法虽然非常有效,但是由于DateFormat类上缺少同步,因此当多个线程访问类变量的相同实例时会引起问题。 解析创建Date对象时抛出的典型异常是:

  • java.lang.NumberFormatException
  • java.lang.ArrayIndexOutOfBoundsException

执行格式化时,您还应该遇到格式错误的“ 日期到字符串”表示形式。

为了正确处理上述问题,弄清多线程环境的体系结构至关重要。 Java虚拟机允许应用程序具有多个并发运行的执行线程。 通常,在多线程环境(JVM内的容器或JVM本身)中,应执行线程池。 工作线程应在启动时构造并初始化,以用于执行程序。 例如,一个Web容器构造了一个工作线程池来服务所有传入的流量。 线程池是处理系统资源的最有效方法,主要是因为线程创建和初始化对于Java虚拟机来说是一项非常耗资源的任务。 不过,只需为要同时执行的每段代码创建一个新的执行线程 ,即可实现应用程序并行性。

关于类范围的DateFormat实例:

以下是“ getDateInstance(..)”,“同步”和ThreadLocal方法的示例:

package com.javacodegeeks.test;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ConcurrentDateFormatAccess {

 public Date convertStringToDate(String dateString) throws ParseException {
  return SimpleDateFormat.getDateInstance(DateFormat.MEDIUM).parse(dateString);
 }

}
package com.javacodegeeks.test;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ConcurrentDateFormatAccess {

 private DateFormat df = new SimpleDateFormat("yyyy MM dd");

 public Date convertStringToDate(String dateString) throws ParseException {
  Date result;
  synchronized(df) {
   result = df.parse(dateString);
  }
  return result;
 }

}

这里要注意的事情:

  • 每个执行“ convertStringToDate”操作的线程都试图在获取对DateFormat类变量instance的引用之前获取DateFormat对象的监视器锁定。 如果另一个线程持有该锁,则当前线程将等待直到释放该锁。 这样,一次只有一个线程正在访问DateFormat实例。
    package com.javacodegeeks.test;
    
    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class ConcurrentDateFormatAccess {
    
     private ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat> () {
    
      @Override
      public DateFormat get() {
       return super.get();
      }
    
      @Override
      protected DateFormat initialValue() {
       return new SimpleDateFormat("yyyy MM dd");
      }
    
      @Override
      public void remove() {
       super.remove();
      }
    
      @Override
      public void set(DateFormat value) {
       super.set(value);
      }
    
     };
    
     public Date convertStringToDate(String dateString) throws ParseException {
      return df.get().parse(dateString);
     }
    
    }

    这里要注意的事情:

    • 每个执行“ convertStringToDate”操作的线程都将调用“ df.get()”操作,以初始化或检索其本地范围内的DateFormat实例的已初始化引用。

    下面,我们给出上述三种方法之间的性能比较表(注意,我们已经测试了DateFormat实用工具类的解析功能。根据特定的日期格式,我们将日期的String表示形式转换为等效的Date对象)。

    横轴表示测试运行的次数,纵轴表示每次测试运行的每秒平均事务数(TPS)。 因此,较高的值更好。 如您所见,通过使用线程池和ThreadLocal方法,与“同步”方法和“ getDateInstance(..)”方法相比,您可以获得更高的性能。

    最后,我要指出的是,使用不带线程池的ThreadLocal方法等同于使用“ getDateInstance(..)”方法,因为每个新线程都必须在使用之前初始化其本地DateFormat实例,因此需要一个新的DateFormat实例将在每次执行时创建。

    编码愉快!

    贾斯汀

    相关文章 :

    翻译自: https://www.javacodegeeks.com/2010/07/java-best-practices-dateformat-in.html

    • 0
      点赞
    • 1
      收藏
      觉得还不错? 一键收藏
    • 0
      评论

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值