Find-Bugs工具抱怨“从实例方法someClassMethod写入静态字段someArrayName和someVariableName”。似乎这不是好的做法(让类实例方法写入静态字段)。是否有好的建议如何解决这个问题?谢谢。
ST:从实例方法写入静态字段(ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD)
这个实例方法写入了一个静态字段。如果正在操作多个实例,这很难得到正确的结果,并且通常是不好的做法。
除了并发问题之外,这意味着JVM中的所有实例都在访问相同的数据,并且不允许两个独立的实例组。最好使用单例"manager"对象,并将其作为构造函数参数或至少作为setManager()
方法参数传递给每个实例。
关于并发问题:如果必须使用静态字段,则应将静态字段设置为final;显式同步是困难的。(还有一些棘手的方面,如果您初始化非final静态字段,在Java Puzzlers书中可以看到,而这超出了我的Java知识范围。)有至少三种处理这种情况的方法(警告,未经测试的代码如下,请先检查再使用):
Use a thread-safe collection, e.g. Collections.synchronizedList
wrapped around a list that is not accessed in any other way.
static final List<Item> items = createThreadSafeCollection();
static List<Item> createThreadSafeCollection()
{
return Collections.synchronizedList(new ArrayList());
}
and then later when you are replacing this collection, from an instance:
List<Item> newItems = getNewListFromSomewhere();
items.clear();
items.add(newItems);
The problem with this is that if two instances are doing this sequence at the same time, you could get:
Instance1: items.clear(); Instance2: items.clear(); Instance1: items.addAll(newItems); Instance2: items.addAll(newItems);
and get a list that doesn't meet the desired class invariant, namely that you have two groups of newItems in the static list. So this method doesn't work if you are clearing the entire list as one step, and adding items as a second step. (If your instances just need to add an item, though, items.add(newItem)
would be safe to use from each instance.)
Synchronize access to the collection.
You'll need an explicit mechanism for synchronizing here. Synchronized methods won't work because they synchronize on "this", which is not common between the instances. You could use:
static final private Object lock = new Object();
static volatile private List<Item> list;
// technically "list" doesn't need to be final if you
// make sure you synchronize properly around unit operations.
static void setList(List<Item> newList)
{
synchronized(lock)
{
list = newList;
}
}
use AtomicReference
static final private AtomicReference<List<Item>> list;
static void setList(List<Item> newList)
{
list.set(newList);
}
如果我正确理解了您从Find Bugs发布的消息,那么这只是一个警告。
如果您想隐藏警告,请从静态方法进行修改。Find Bugs正在警告您,因为这通常是一个错误。程序员认为他们正在更改某些实例状态,但实际上他们正在更改影响每个实例的某些状态。
使用Singleton设计模式是一种方法。您可以只有一个包含所需值的对象实例,并通过全局属性访问该实例。优点是,如果以后想要更多实例,则需要修改现有代码的量较少(因为您不会将静态字段更改为实例字段)。
您不需要每次删除列表。根据上面的内容,您将不得不处理多个线程,但是您可以创建ArrayList一次,然后使用clear()和addAll()方法来清除和重新填充。FindBugs应该会很满意,因为您没有设置静态变量。
如果这种技术有任何问题,请随时提出建议 :-)
第二个想法是通过Hibernate从数据库驱动事物。因此,不要维护列表,Hibernate具有内置缓存,因此速度几乎相同。如果您在数据库级别更新数据(这意味着Hibernate不知道),则可以告诉Hibernate清除其缓存并在下次查询时从数据库刷新。
你不想这样做。每个请求都在自己的线程中运行。如果在浏览器操作上执行的代码修改了列表,那么两个请求可能会同时修改列表,并破坏数据。这就是为什么从非静态上下文访问静态资源不是一个好主意,也可能是你的工具警告你的原因。
看看这个
http://download.oracle.com/javase/6/docs/api/index.html?java/util/concurrent/package-summary.html
特别是关于ArrayList未同步的部分。还要注意,我提到的段落有一个解决方案,具体来说。List list = Collections.synchronizedList(new ArrayList(...));
这是一种解决方法。但这仍然不是一个好主意,因为它可能会很慢。如果这不是商业级应用程序,并且您没有处理大量数据,那么您可以忽略警告,如果两个请求相互干扰,则可能会发生一些问题。
更好的解决方案:由于您有数据库,因此我建议您根据需要从数据库中获取信息,即随着请求的到来而进行。您可以使用一些缓存技术来提高性能。
我不喜欢单例模式的原因是,即使它消除了警告,它本身并没有解决基本的同步问题。然而,在这种情况下,有一些线程安全的http://en.wikipedia.org/wiki/Singleton_pattern#Traditional_simple_way_using_synchronization,这可能有效。