2016-09-23 52 views
0

我见过的ThreadLocal的每个实例返回不能动态地设置,类似这样的例子有SimpleDateFormat的,每次它总是返回相同的SimpleDateFormat值:ThreadLocal的初始化

public class Foo 
{ 
    // SimpleDateFormat is not thread-safe, so give one to each thread 
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){ 
     @Override 
     protected SimpleDateFormat initialValue() 
     { 
      return new SimpleDateFormat("yyyyMMdd HHmm"); 
     } 
    }; 

    public String formatIt(Date date) 
    { 
     return formatter.get().format(date); 
    } 
} 

但可以说,我希望能够配置返回的值。一种方法是使用系统属性是这样的:

public class Foo 
{ 
    // SimpleDateFormat is not thread-safe, so give one to each thread 
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){ 
     @Override 
     protected SimpleDateFormat initialValue() 
     { 
      String dateFormat = System.getProperty("date.format"); 
      return new SimpleDateFormat(dateFormat); 
     } 
    }; 

    public String formatIt(Date date) 
    { 
     return formatter.get().format(date); 
    } 
} 

但是,如果我不希望使用系统属性,而是想用它创建时的必要信息提供类的东西。我怎么做。一切都是静态的,所以我不能使用构造函数。

我不喜欢系统属性方法的原因很多。对于一个我不想让这个班级了解其周围环境的东西,那就是应该阅读的系统属性。它应该尽可能简单,并注入所有的依赖关系。例如,我认为这种编码方式可以提高可测性。

最终解决

格式是通过调用setFormat和formatIt所有来电设置一次后,使用相同的格式。

public class Foo { 

    private static volatile String FORMAT; 

    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>() { 
     @Override 
     protected SimpleDateFormat initialValue() { 
      return new SimpleDateFormat(FORMAT); 
     } 
    }; 

    /** 
    * Set the format. Must be called before {@link #formatIt(Date)}. Must only be called once. 
    * 
    * @param format 
    *   a format, e.g. "yyyyMMdd HHmm". 
    * @throws IllegalStateException 
    *    if this method has already been called. 
    */ 
    public static void setFormat(String format) { 
     if (Foo.FORMAT != null) { 
      throw new IllegalStateException("Format has already been set"); 
     } 
     FORMAT = format; 
    } 

    /** 
    * @return the formatted date. 
    * @throws IllegalStateException 
    *    if this method is called before {@link #setFormat(String)} has been called. 
    */ 
    public static String formatIt(Date date) { 
     if (Foo.FORMAT == null) { 
      throw new IllegalStateException("Format has not been set"); 
     } 
     return formatter.get().format(date); 
    } 

} 
+0

你用java 8吗? –

+0

不,我使用Java 1.6。 – Mattias

+0

Wy的投票?我对此进行了广泛的研究,并且我是一位经验丰富的程序员。这不像我在学校,只是想让你们帮我完成作业。这将用于生产。如果给我弃权的人认为这是一个微不足道的问题,请提供答案。 – Mattias

回答

2

当你不使用DI框架,对我来说唯一的办法就是增加一个static二传手允许动态改变您的格式,这样的事情:

public class Foo { 
    private static volatile String FORMAT = "yyyyMMdd HHmm"; 

    // SimpleDateFormat is not thread-safe, so give one to each thread 
    private static final ThreadLocal<SimpleDateFormat> formatter = 
     new ThreadLocal<SimpleDateFormat>(){ 
     @Override 
     protected SimpleDateFormat initialValue() { 
      return new SimpleDateFormat(FORMAT); 
     } 
    }; 

    public static void setFormat(String format) { 
     FORMAT = format; 
    } 
    ... 
} 

这是很丑陋但我没有看到更好的方法。


在这个特殊的使用情况下,当你使用Java 6,我显然会建议使用的Joda-Time,而不是SimpleDateFormat,因为它做同样的事情DateTimeFormatter,它是线程安全的开箱即用。

这里是你的代码会是什么样子:

public class Foo { 

    private static volatile DateTimeFormatter formatter = 
     DateTimeFormat.forPattern("yyyyMMdd HHmm");; 

    public String formatIt(Date date) { 
     return formatter.print(date.getTime()); 
    } 

    public static void setFormat(String format) { 
     formatter = DateTimeFormat.forPattern(format); 
    } 
} 

正如你可以看到的是没有更多的需要ThreadLocalDateTimeFormatter是线程安全的。

注:我认为你需要设置格式全球Foo所有情况下,如果不是的话,你倒是应该这样做:

public class Foo { 

    private volatile DateTimeFormatter formatter = 
     DateTimeFormat.forPattern("yyyyMMdd HHmm");; 

    public String formatIt(Date date) { 
     return formatter.print(date.getTime()); 
    } 

    public void setFormat(String format) { 
     this.formatter = DateTimeFormat.forPattern(format); 
    } 
} 

用这种方法你注入更多面向对象方法的格式。

+0

这实际上比以前简单得多,当我累了的时候我不应该考虑threadlocals。 – Kayaman

+0

@Kayaman你应该考虑休息一下然后:-) –

+0

@Nicolas谢谢你的建议解决方案。但是,这种解决方案与以前由Kayaman提出的解决方案不同吗? – Mattias