如何知道Java SE类或方法是否是线程安全的?

时间:2015-08-15 09:23:36

标签: java multithreading thread-safety

例如:

static private DateFormat df = new SimpleDateFormat();
public static void format(final Date date) { 
   for (int i = 0; i < 10; i++) 
     new Thread(new Runnable() {
         public void run() {
             System.out.println(df.format(date));
         } 
     });
}

DateFormat类被记录为不是同步类,但如果我们只使用格式方法,它就不能改变整个类的状态?

假设它被声明为私有,如何确保代码是线程安全的?

修复此代码的最佳方法是什么?:

  1. 为每个帖子使用不同的实例。

  2. 使用同步块。

3 个答案:

答案 0 :(得分:4)

  • 对于标准Java SE类,了解该类是否是线程安全的最佳方法是仔细阅读其文档。始终阅读课程文档和方法文档。如果要么说它不同步或不是线程安全的,那么你知道它不是线程安全的。
  • 因此,DateFormat线程安全。文档具体说:

      

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

  • 声明字段private使使您的实现成为线程安全的。 private只是说外面的课程无法看到该字段。让我们来看看你的方法:

     for (int i=0;i<10;i++) 
         new Thread(new Runnable(){
             public void run(){
                 System.out.println(df.format(date));
             } 
         });
    

    您创建的Runnable个对象是匿名类。匿名类是内部类,可以访问其周围类的私有字段。如果不是这样,您的程序将无法编译 - 他们无法访问df字段。

    但他们可以。所以实际上你有10个线程都在访问你的DateFormat对象,由df引用。由于我们已经知道DateFormat 是线程安全的,因此您的程序不是线程安全的。

  • 此外,如果两个外部线程都引用了你的对象(我的意思是里面有df的对象。你没有给出类声明所以我不知道它是什么名字是)。它们引用了同一个类的实例。如果他们两个同时拨打format,则两个人都将使用相同的私有DateFormat.format运行df。因此,这不是线程安全的。
  • 要成为线程安全的,您需要在对象上进行同步或使用其他类型的锁(对于访问它的所有可能线程都有一个锁),这正是文档所说的。
  • 另一种方法是拥有一个完全本地的对象,该对象只对一个线程可见。不是字段 - 一个局部变量,可以访问唯一创建的DateFormat实例(因此每次调用方法时都有一个新副本)。但要注意匿名课程!在您的示例中,即使dfformat方法的本地字段,它仍然不是线程安全的,因为您的所有线程都将访问同一副本。

答案 1 :(得分:3)

根据文档,声明格式不是线程安全的。

<强>同步

  

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

Date format

如何阅读?如果您没有明确保证某些方法是线程安全的(或者它被记录为不安全),那么您就不能对其安全做出任何假设。

但是,如果你真的希望只使用可能不是有状态的单一方法,那么你可以随时创建高并发环境,并在有和没有同步的情况下测试数据完整性。

我对此有类似的问题,使用Ciphers和RSA。 答案显示了一般如何测试java SE类的特定方法的一种方法。但请注意,实现可能随时发生变化,并且通过针对实现细节进行自己的实现,而不是接口可能会在将来导致一些不可预测的问题。

testing for data integrity

答案 2 :(得分:2)

我知道很难相信,但DateFormat.format()实际上修改了DateFormat的状态。例如,对于SimpleDateFormat:

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

其中calendar是DateFormat的字段。

因此,我只建议您信任文档。它可能知道你不知道的事情。