我有一个相当简单的静态变量问题。我正在构建一个松散地遵循路径或RMI的解决方案。在我的服务器上,我有一个ComputeEngine类,它将执行'Tasks'(带有'execute'方法的类实例)。但是,ComputeEngine将包含一个全局变量,需要由不同的任务访问,每个任务都在自己的线程中执行。提供访问权限的最佳方式是什么?我希望尽可能保持松散耦合的一切。我的ComputeEngine类中的共享全局静态变量将是一个List。我应该为这个静态变量获取一个getter吗?我将在ComputeEngine类中使用读/写锁来访问我的全局List。这也是静态的,需要共享。我正在寻找关于如何在类中提供对全局静态变量的访问的最佳实践。
答案 0 :(得分:4)
如果要将其解耦,最好的方法是在创建Task
时传递回调对象。
interface FooListManipulator {
void addFoo( Foo f );
List<Foo> getFooList();
}
class Task {
private FooListManipulator fooListManipulator;
public Task( FooListManipulator fooListManipulator ) {
this.fooListManipulator = fooListManipulator;
}
}
这样,任务本身不必假设有关创建它的人以及列表的存储方式。
在你的ComputeEngine
中,你会做这样的事情:
class ComputeEngine {
private static List<Foo> fooList;
class Manipulator implements FooListManipulator {
public void addFoo( Foo f ) {
synchronized( fooList ) {
fooList.add( f );
}
}
public List<Foo> getFooList() {
return Collections.unmodifiableList( fooList );
}
}
private Task createTask() {
return new Task( new Manipulator() );
}
}
如果您想稍后更改fooList
的存储空间(您应该考虑,因为静态全局变量不是一个好主意),Task
将保持不变。另外,您可以使用模拟操纵器对Task
进行单元测试。
答案 1 :(得分:2)
我正在寻找最佳实践方法 提供对全局静态的访问 变量
通过最佳实践,您不应该有这样的变量。
我应该为这个静态获得一个吸气剂 变量?我会读/写 锁定我的ComputeEngine类给出 访问我的全球名单。
不,你不应该提供这样的吸气剂。只需addTask(Task task)
或execute(task)
方法。方法同步将是可行的解决方案。
答案 2 :(得分:2)
Nooooooooooooooooooooooooooooooooooooooooooooooooooo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
静态变量是坏的,并且因为单身人士使事情变得更糟。必要时通过构造函数传递对象。并给予对象明智的行为。
对于RMI,默认情况下,您从客户端指示的任何位置加载不受信任的代码(顶端提示,使用RMI时,使用-Djava.rmi.server.useCodebaseOnly=true
)。作为一个全局静态,这段代码可以摆弄你的服务器状态(假设在一个可访问的类加载器等)。
答案 3 :(得分:1)
不要从getter返回你的列表,因为你不知道人们会用它做什么(他们可能会添加内容并破坏你的锁定)。所以这样做:
static synchronized List getTheList(){ 返回new ArrayList(theList); }
并且只有在任何人真正需要时才实现getter
除此之外,拥抱全局静态变量是不受欢迎的......
答案 4 :(得分:1)
我有几条建议:
答案 5 :(得分:1)
追求@biziclop回答,但与其他分离)
您可以在下一部分中分隔您的代码。
interface Task {
void execute();
}
public final class TaskExecutor{
TaskExecutor(List<Task> tasks){}
void addTask(Task task){synchronized(tasks){tasks.add(task);}}
}
比,
public class SomeTaskAdder {
SomeTaskAdder(TaskExecutor executor){}
void foo(){
executor.addTask(new GoodTask(bla-bla));
}
}
public class SomeTasksUser {
SomeTasksUser(List<Task> tasks){synchronized(tasks){bla-bla}}
}
,你应该用一些魔术构造函数注入来创建你的对象)
答案 6 :(得分:1)
每个人似乎都断言你正在使用List
来保留任务队列,但我实际上并没有在你的问题中看到这一点。但如果是,或者对列表的操作是否独立 - 也就是说,如果您只是添加或从列表中删除,而不是说,扫描列表并从中间删除一些项目作为工作 - 那么你应该考虑使用BlockingQueue
或QueueDeque
而不是List
,而只是利用java.util.concurrent
包。这些类型不需要外部锁管理。
如果您认真地要求每个作业同时访问的List
,其中对列表的读取和写入不是独立的,我将封装处理的一部分,在单例中执行此操作,并使用独占锁使每个线程使用该列表。例如,如果您的列表包含某种只是流程执行的一部分的聚合统计信息,那么我的工作就是一个类,单个聚合统计信息就是一个单独的工作。
class AggregateStatistics {
private static final AggregateStatistics aggregateStatistics =
new AggregateStatistics();
public static AggregateStatistics getAggregateStatistics () {
return aggregateStatistics;
}
private List list = new ArrayList ();
private Lock lock = new ReentrantLock();
public void updateAggregates (...) {
lock.lock();
try {
/* Mutation of the list */
}
finally {
lock.unlock();
}
}
}
然后让你的任务通过访问单例并调用使用锁管理的方法来进入作业的这一部分。
从不将一个集合传递到并发环境中,它只会导致问题。通过使用java.util.Collections.unmodifiableList(List)
和类似的方法,你总是可以传递一个不可变的“包装器”,如果它真的合适的话。