【问题标题】:Protecting a static class variable保护静态类变量
【发布时间】:2011-03-02 20:10:25
【问题描述】:

我有一个相当琐碎的静态变量问题。我正在构建一个松散地遵循路径或 RMI 的解决方案。在我的服务器上,我有一个 ComputeEngine 类,它将执行“任务”(具有“执行”方法的类实例)。但是,ComputeEngine 将包含一个全局变量,需要由不同的任务访问,每个任务都在自己的线程中执行。授予访问权限的最佳方式是什么?我想保持一切尽可能松散耦合。我的 ComputeEngine 类中的共享全局静态变量将是一个列表。我应该有这个静态变量的吸气剂吗?我将在我的 ComputeEngine 类中有一个读/写锁,以授予对我的全局列表的访问权限。这也将是静态的,需要共享。我正在寻找有关如何在类中提供对全局静态变量的访问的最佳实践。

【问题讨论】:

  • 共享列表应该有哪些不同的线程?只读取它还是写入它?
  • 您好。不同的线程将从列表中读取一个对象并更新它们。谢谢和问候
  • “更新”是什么意思?
  • 您好。通过更新,我的意思是调用一个可能会更新对象中的变量的方法。问候

标签: java variables static


【解决方案1】:

如果要解耦,最好的方法是在创建Task时传递一个回调对象。

interface FooListManipulator {
  void addFoo( Foo f );
  List<Foo> getFooList();
}

class Task {
  private FooListManipulator fooListManipulator;

  public Task( FooListManipulator fooListManipulator ) {
    this.fooListManipulator = fooListManipulator;
  }
}

这样,Task 本身就不必假设是谁创建了它以及列表是如何存储的。

在您的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 进行单元测试。

【讨论】:

  • 嗨,biziclop,谢谢。这是一个很好的方法。最好的问候。
  • 更进一步,如果 Manipulator 只处理列表中的一件事情,它甚至不必知道它是一个列表。再次使用接口,您可以让 ComputeEngine 将要处理的一个对象交给 Manipulator。
  • @Stephen P 没错,我们的想法是只公开任务需要执行的实际操作。只是我在写这个答案时没有看到 OP 的额外 cmets。
【解决方案2】:

我正在寻找有关如何 提供对全局静态的访问 变量

按照最佳做法,您不应该有这样的变量。

我应该为这个静态设置一个吸气剂吗 多变的?我将有一个读/写 锁定我的 ComputeEngine 课程 访问我的全局列表。

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

【讨论】:

  • 您可以提供一个 getter,但它会返回包含在 Collections.unmodifiableList() 中的列表。
  • @biziclop,正确的你可以使用不可修改的集合甚至不可变的集合与番石榴,但是,在“收集器”类中实现任务处理程序逻辑可能会更好。当然,这取决于应用程序的种类。
【解决方案3】:

不不不不不不不不不不不不不不不不不不!!!!!!!!!!!!!!!!!!!!!!!!!!!!

静态变量是不好的,把它打扮成单例只会让事情变得更糟。根据需要通过构造函数传递对象。并赋予对象合理的行为。

在 RMI 的情况下,默认情况下,您从客户端指示的任何位置加载不受信任的代码(最重要的提示,使用 RMI 时,请使用 -Djava.rmi.server.useCodebaseOnly=true)。作为全局静态代码,此代码可能会影响您的服务器状态(假设在可访问的类加载器中等)。

【讨论】:

  • 嗨,Tom,就我而言,安全性根本不是问题。这不会是商业应用。只是一个掌握 RPC 的个人项目。干杯
【解决方案4】:
  • 不要从 getter 返回您的列表,因为您不知道人们会用它做什么(他们可能会添加东西并破坏您的锁定)。这样做:

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

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

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

除此之外,拥有一个全局静态变量是不受欢迎的......

【讨论】:

    【解决方案5】:

    我有几个建议给你:

    1. 您的“Task”听起来像 Runnable,将“execute”更改为“run”,您将免费获得很多东西。就像 java.util.concurrent 中所有很棒的类一样。
    2. 通过the technique in this post 使 ComputeEngine 本身成为单例。为了清楚起见,请使用 Josh Bloch 的“枚举”方法(该问题的第二个答案)。
    3. 让您的列表成为 ComputeEngine 的成员
    4. 任务使用修改列表的 ComputeEngine.saveResult(...)。
    5. 考虑使用 java.util.concurrent.Executors 来管理您的任务池。

    【讨论】:

      【解决方案6】:

      追求@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}}
      }
      

      比,你应该用一些神奇的构造函数注入来创建你的对象)

      【讨论】:

      • 此外,您可以将 List 包装到同步集合中。
      【解决方案7】:

      似乎每个人都在断言您正在使用List 来保留任务队列,但我实际上并没有在您的问题中看到这一点。但如果是这样,或者对列表的操作是独立的——也就是说,如果您只是简单地添加到列表中或从列表中删除,而不是说,扫描列表并从中间删除一些项目作为工作 - 那么您应该考虑使用 BlockingQueueQueueDeque 而不是 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) 和类似方法传递一个不可变的“包装器”,但如果它真的合适的话。

      【讨论】:

        猜你喜欢
        • 2011-05-15
        • 1970-01-01
        • 2020-03-29
        • 2016-03-08
        • 1970-01-01
        • 2018-12-03
        • 2012-07-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多