Java 保护静态类变量

Java 保护静态类变量,java,variables,static,Java,Variables,Static,我有一个相当简单的静态变量问题。我正在构建一个松散遵循路径或RMI的解决方案。在我的服务器上,我有一个ComputeEngine类,它将执行“Tasks”(带有“execute”方法的类实例)。但是,ComputeEngine将包含一个全局变量,需要由不同的任务访问该变量,每个任务都在自己的线程中执行。访问此文件的最佳方式是什么?我想让一切尽可能松散耦合。ComputeEngine类中的共享全局静态变量将是一个列表。我应该为这个静态变量设置一个getter吗?我的ComputeEngine类中有

我有一个相当简单的静态变量问题。我正在构建一个松散遵循路径或RMI的解决方案。在我的服务器上,我有一个ComputeEngine类,它将执行“Tasks”(带有“execute”方法的类实例)。但是,ComputeEngine将包含一个全局变量,需要由不同的任务访问该变量,每个任务都在自己的线程中执行。访问此文件的最佳方式是什么?我想让一切尽可能松散耦合。ComputeEngine类中的共享全局静态变量将是一个列表。我应该为这个静态变量设置一个getter吗?我的ComputeEngine类中有一个读/写锁,可以访问我的全局列表。这也是静态的,需要共享。我正在寻找关于如何在类中提供对全局静态变量的访问的最佳实践

我正在寻找最佳的实践方法 提供对全局静态文件的访问 变数

根据最佳实践,您不应该有这样的变量

我应该为这个静态设置一个getter吗 变量我要读/写一篇文章 锁定我的ComputeEngine类 访问我的全局列表

不,你不应该提供这样的getter。只需
addTask(Task Task)
execute(Task)
方法即可。方法同步将是可行的解决方案。

  • 不要从getter返回您的列表,因为您不知道人们会如何处理它(他们可能会添加内容并破坏您的锁定)。这样做:

    静态同步列表getTheList(){ 返回新的ArrayList(列表); }

只有在有人真正需要时才实现getter

  • 不要执行任何设置器;而是实现addItemToList()和removeItemToList()

除此之外,不赞成使用全局静态变量…

我有几点建议:

  • 你的“任务”听起来像是可运行的,把“执行”改为“运行”,你可以免费得到很多东西。就像java.util.concurrent中所有很棒的类一样
  • 使ComputeEngine本身成为一个单例via。为了清楚起见,请使用Josh Bloch的“枚举”方法(关于该问题的第二个答案)
  • 让您的列表成为ComputeEngine的成员
  • 任务使用ComputeEngine.saveResult(…),它修改列表
  • 考虑使用java.util.concurrent.Executors来管理任务池

  • 如果要将其解耦,最好的方法是在创建
    任务时传递回调对象

    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();
            }
        }
    }