我可以使用AspectJ使用ThreadLocal“包装”字段吗?

时间:2013-02-27 16:49:53

标签: java thread-safety aspectj

假设我们有一个如下所示的类。

public class MyClass {

   @ThreadScoped    
   private Collection<String> words = new ArrayList<String>();

   public void addWord(String word) {
        words.add(word);
   }

}

在运行时,我们只有这个类的一个实例,但它接受来自多个线程的调用。

我希望编写该类的开发人员能够忽略他们为多线程访问编写它的事实,并且只是将该字段注释为“线程范围”。

是否可以创建一个方面,使每个调用线程都有自己的单词集合,就好像单词字段是ThreadLocal一样?即使在方法的多次调用中,该集合也应该与线程绑定。

我已经看到setget点切割,但无法找到一种方法来使用它们,这不需要我在对象上设置字段,这会破坏线程班上的安全。

是否有其他方法可以做到不利用方面,例如使用代理或反射?

2 个答案:

答案 0 :(得分:2)

您的设计中似乎存在一些可能存在冲突的决定:

  • 您只有一个MyClass实例。

  • 您希望每个帖子都能查看自己的words字段版本。

在我看来,为此目的使用AspectJ可能有点矫枉过正。我认为特定于线程的访问可能被视为一个跨领域的问题,但我不确定AOP是解决这个问题的方法。在尝试使用AspectJ之前,我首先考虑重新设计纯Java代码。

老实说,我相信实现第二个效果的最简单方法是每个线程都有自己的MyClass实例。您甚至可以使用ThreadLocal来提供对正确的MyClass实例的访问,如果显式地将它传递给每个线程都不是一个选项。

如果MyClass中的字段由多个线程共享,那么您可能需要考虑重构MyClass以将其分解为两个类。这会将特定于线程的内容与共享字段分开,从而允许您以不同方式处理每个案例。

修改

我想到了你的评论,在我看来,你试图使用AOP从本质上隐藏开发人员的字段实现。将编程接口与实际实现分离的问题主要在面向对象的编程中得到解决,而不是解析为AOP。

在你的情况下,方法是使用MyClass的抽象超类来保存该字段的私有声明,然后使用几个getter和setter方法让let MyClass使用它。这样你就可以改变实际的实现而不会影响其他任何东西。

public abstract AbstractMyClass {
    private ... words = ...;

    protected Collection<String> getWords() {
        ...
    }
}

public Myclass extends AbstractMyClass {
   public void addWord(String word) {
        this.getWords().add(word);
   }
}

答案 1 :(得分:1)

出于以下几个原因,使用AspectJ无法使用AspectJ:

  • 在创建时没有通用的克隆对象的方法,甚至对于集合也没有。然后考虑原始类型。
  • 即使您将对象克隆到特定ThreadLocal切入点中的set(),如何将对象上的方法调用重定向到克隆?

话虽如此,你可以选择一种方法来编写一个源代码预处理器来做一些魔法(包装有问题的对象和方法调用它们)。预处理器会将其代码编织到源代码中,然后通常使用 javac 编译它们。

无论如何,正如thkala所说,我认为最简单的解决方案是教你的开发人员自己实际使用ThreadLocal或使用你提供的API(你必须实现)的脚踏局部包装器。