Java 保护静态类变量
我有一个相当简单的静态变量问题。我正在构建一个松散遵循路径或RMI的解决方案。在我的服务器上,我有一个ComputeEngine类,它将执行“Tasks”(带有“execute”方法的类实例)。但是,ComputeEngine将包含一个全局变量,需要由不同的任务访问该变量,每个任务都在自己的线程中执行。访问此文件的最佳方式是什么?我想让一切尽可能松散耦合。ComputeEngine类中的共享全局静态变量将是一个列表。我应该为这个静态变量设置一个getter吗?我的ComputeEngine类中有一个读/写锁,可以访问我的全局列表。这也是静态的,需要共享。我正在寻找关于如何在类中提供对全局静态变量的访问的最佳实践 我正在寻找最佳的实践方法 提供对全局静态文件的访问 变数 根据最佳实践,您不应该有这样的变量 我应该为这个静态设置一个getter吗 变量我要读/写一篇文章 锁定我的ComputeEngine类 访问我的全局列表 不,你不应该提供这样的getter。只需Java 保护静态类变量,java,variables,static,Java,Variables,Static,我有一个相当简单的静态变量问题。我正在构建一个松散遵循路径或RMI的解决方案。在我的服务器上,我有一个ComputeEngine类,它将执行“Tasks”(带有“execute”方法的类实例)。但是,ComputeEngine将包含一个全局变量,需要由不同的任务访问该变量,每个任务都在自己的线程中执行。访问此文件的最佳方式是什么?我想让一切尽可能松散耦合。ComputeEngine类中的共享全局静态变量将是一个列表。我应该为这个静态变量设置一个getter吗?我的ComputeEngine类中有
addTask(Task Task)
或execute(Task)
方法即可。方法同步将是可行的解决方案。- 不要从getter返回您的列表,因为您不知道人们会如何处理它(他们可能会添加内容并破坏您的锁定)。这样做: 静态同步列表getTheList(){ 返回新的ArrayList(列表); }
- 不要执行任何设置器;而是实现addItemToList()和removeItemToList()
除此之外,不赞成使用全局静态变量…我有几点建议:
如果要将其解耦,最好的方法是在创建
任务时传递回调对象
interface FooListManipulator {
void addFoo( Foo f );
List<Foo> getFooList();
}
class Task {
private FooListManipulator fooListManipulator;
public Task( FooListManipulator fooListManipulator ) {
this.fooListManipulator = fooListManipulator;
}
}
如果您想更改<代码> >“愚蠢者> /代码>以后的存储(您应该真正考虑到,作为静态全局变量不是一个好主意),<代码>任务< /代码>将保持不变。此外,您还可以使用模拟操纵器对任务进行单元测试。
在@biziclop answer之后进行单元测试,但需要进行其他分离)
您可以在接下来的部分中分离代码
interface Task {
void execute();
}
public final class TaskExecutor{
TaskExecutor(List<Task> tasks){}
void addTask(Task task){synchronized(tasks){tasks.add(task);}}
}
接口任务{
void execute();
}
公共最终类任务执行器{
任务执行器(列出任务){}
void addTask(任务任务){已同步(任务){tasks.add(任务);}
}
比
公共类SomeTaskAdder{
SomeTaskAdder(TaskExecutor executor){}
void foo(){
执行人:addTask(新的GoodTask(bla-bla));
}
}
公共类SomeTasksUser{
SomeTasksUser(列出任务){synchronized(任务){bla bla}}
}
然后,您应该使用一些神奇的构造函数注入来创建对象)每个人似乎都认为您使用列表来保持任务队列,但我在您的问题中没有看到这一点。但是如果是,或者如果对列表的操作在其他方面是独立的-也就是说,如果你只是简单地向列表中添加或从列表中删除,而不是说,扫描列表并从中间删除一些项目作为作业的一部分-然后,您应该考虑使用<代码>阻塞队列> />代码>或<代码> QueueDeque >代码>,而不是<代码>列表>代码>,并简单地使用 java .UTI.Outury包。这些类型不需要外部锁管理
如果您确实需要一个列表
,该列表由每个作业同时访问,其中对列表的读写不是独立的,那么我会将执行此操作的处理部分封装在一个单例中,并使用一个排他锁让每个线程使用该列表。例如,如果您的列表包含某种聚合统计信息,而这些统计信息只是流程执行的一部分,那么我的作业将是一个类,而单例聚合统计信息将是一个单独的作业
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();
}
}
}
然后让您的任务通过访问singleton并调用其上用锁管理的方法来输入作业的这一部分
永远不要将一个集合传递到并发环境中,它只会给您带来问题。通过使用java.util.Collections.unmodifiableList(List)
和类似的方法,你总是可以传递一个不可变的“包装器”,尽管它确实合适。nooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
静态可变项是不好的,把它打扮成单例只会让事情变得更糟。根据需要通过构造函数传递对象。并给予对象合理的行为
在RMI的情况下,默认情况下,您从客户机指示的任何位置加载不受信任的代码(顶部提示,使用RMI时,请使用-Djava.RMI.server.useCodebaseOnly=true
)。作为一个全局静态代码,此代码可以修改您的服务器状态(假设在可访问的类加载器中等)。不同的线程应该如何处理共享列表?只读它还是给它写信?你好。不同的线程将从列表中读取对象并更新它们。谢谢和尊敬“更新”是什么意思?你好。通过更新,我的意思是调用可能会更新对象中变量的方法。考虑到你可以提供一个getter,尽管这个retu
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}}
}
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();
}
}
}