一次初始化所有元数据

时间:2016-11-24 16:09:49

标签: java multithreading

我有一个连接到Cassandra的Singleton类。我想一次性初始化processMetadataprocMetadatatopicMetadata。如果他们一次初始化,那么我会看到这三个中没有不同值的一致值。

在下面的代码中,processMetadataprocMetadatatopicMetadata首次在initializeMetadata方法中初始化,然后每15分钟更新一次。

public class CassUtil {
  private static final Logger LOGGER = Logger.getInstance(CassUtil.class);
  private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

  // below are my three metedata which I need to update all three at once not one by one
  private List<ProcessMetadata> processMetadata = new ArrayList<>();
  private List<ProcMetadata> procMetadata = new ArrayList<>();
  private List<String> topicMetadata = new ArrayList<>();

  private Session session;
  private Cluster cluster;

  private static class Holder {
    private static final CassUtil INSTANCE = new CassUtil();
  }

  public static CassUtil getInstance() {
    return Holder.INSTANCE;
  }

  private CassUtil() {
    List<String> servers = TestUtils.HOSTNAMES;
    String username = TestUtils.USERNAME;
    String password = TestUtils.PASSWORD;

    PoolingOptions opts = new PoolingOptions();
    opts.setCoreConnectionsPerHost(HostDistance.LOCAL,
        opts.getCoreConnectionsPerHost(HostDistance.LOCAL));

    Builder builder = Cluster.builder();
    cluster =
        builder
            .addContactPoints(servers.toArray(new String[servers.size()]))
            .withRetryPolicy(DowngradingConsistencyRetryPolicy.INSTANCE)
            .withPoolingOptions(opts)
            .withReconnectionPolicy(new ConstantReconnectionPolicy(100L))
            .withLoadBalancingPolicy(
                DCAwareRoundRobinPolicy
                    .builder()
                    .withLocalDc(
                        !TestUtils.isProduction() ? "DC2" : TestUtils.getCurrentLocation()
                            .get().name().toLowerCase()).build())
            .withCredentials(username, password).build();

    try {
      session = cluster.connect("testkeyspace");
    } catch (NoHostAvailableException ex) {
      LOGGER.logError("error= ", ExceptionUtils.getStackTrace(ex));
    } catch (Exception ex) {
      LOGGER.logError("error= " + ExceptionUtils.getStackTrace(ex));
    }
  }

  // start a background thread which runs every 15 minutes
  public void startScheduleTask() {
    scheduler.scheduleAtFixedRate(new Runnable() {
      public void run() {
        try {
          processMetadata = processMetadata(true);
          topicMetadata = listOfTopic(TestUtils.GROUP_ID);
          procMetadata = procMetadata();
        } catch (Exception ex) {
          LOGGER.logError("error= ", ExceptionUtils.getStackTrace(ex));
        }
      }
    }, 0, 15, TimeUnit.MINUTES);
  }

  // called from main thread to initialize the metadata
  // and start the background thread where it gets updated
  // every 15 minutes
  public void initializeMetadata() {
    processMetadata = processMetadata(true);
    topicMetadata = listOfTopic(TestUtils.GROUP_ID);
    procMetadata = procMetadata();
    startScheduleTask();
  }

  private List<String> listOfTopic(final String consumerName) {
    List<String> listOfTopics = new ArrayList<>();
    String sql = "select topics from topic_metadata where id=1 and consumerName=?";
    try {
        // get data from cassandra
    } catch (Exception ex) {
      LOGGER.logError("error= ", ExceptionUtils.getStackTrace(ex), ", Consumer Name= ",
          consumerName);
    }
    return listOfTopics;
  }

  private List<ProcessMetadata> processMetadata(final boolean flag) {
    List<ProcessMetadata> metadatas = new ArrayList<>();
    String sql = "select * from process_metadata where id=1 and is_active=?";
    try {
        // get data from cassandra
    } catch (Exception ex) {
      LOGGER.logError("error= ", ExceptionUtils.getStackTrace(ex), ", active= ", flag);
    }
    return metadatas;
  }

  private List<ProcMetadata> procMetadata() {
    List<ProcMetadata> metadatas = new ArrayList<>();
    String sql = "select * from schema where id=1";
    try {
        // get data from cassandra
    } catch (SchemaParseException ex) {
      LOGGER.logError("schema parsing error= ", ExceptionUtils.getStackTrace(ex));
    } catch (Exception ex) {
      LOGGER.logError("error= ", ExceptionUtils.getStackTrace(ex));
    }
    return metadatas;
  }

  public List<ProcessMetadata> getProcessMetadata() {
    return processMetadata;
  }

  public List<String> getTopicMetadata() {
    return topicMetadata;
  }

  public List<ProcMetadata> getProcMetadata() {
    return procMetadata;
  }
}

因此,在我的主线程中,我只调用initializeMetadata方法一次初始化这三个元数据,然后启动一个后台线程,每15分钟更新一次。从我的多线程开始,我就像使用它们一样:

CassUtil.getInstance().getProcessMetadata();
CassUtil.getInstance().getTopicMetadata();
CassUtil.getInstance().getProcMetadata();

问题陈述: -

现在我想看到processMetadatatopicMetadataprocMetadata的相同状态。这意味着这三个元数据应该同时更新,而不是一个接一个的bcoz我不想看到它们之后的混合状态值。

如何避免此问题?我是否需要创建另一个类来保存这三个元数据作为构造函数参数?

1 个答案:

答案 0 :(得分:1)

保持列表一致状态的最有效方法是使用一个不可变类来保存您的3个列表,然后您将在您的班级中拥有此类型的字段将定义volatile以确保所有线程都能看到该字段的最新更新。

以下是我们用来保存列表状态的不可变类(它可能是一个普通的类,但因为它是特定于实现的,它可能是一个静态内部类): / p>

private static class State {
    private final List<ProcessMetadata> processMetadata;
    private final List<ProcMetadata> procMetadata;
    private final List<String> topicMetadata;

    public State(final List<ProcessMetadata> processMetadata, 
        final List<ProcMetadata> procMetadata, final List<String> topicMetadata) {
        this.processMetadata = new ArrayList<>(processMetadata);
        this.procMetadata = new ArrayList<>(procMetadata);
        this.topicMetadata = new ArrayList<>(topicMetadata);
    }
    // Getters
}

那么你的课就是这样的:

public class CassUtil {
    ...
    private volatile State state = new State(
        new ArrayList<>(), new ArrayList<>(), new ArrayList<>()
    );
    ...
    public void startScheduleTask() {
        ...
                    this.state = new State(
                        processMetadata(true), listOfTopic(TestUtils.GROUP_ID), 
                        procMetadata()
                    );
        ...
    }
    ...
    public void initializeMetadata() {
        this.state = new State(
            processMetadata(true), listOfTopic(TestUtils.GROUP_ID), procMetadata()
        );
        startScheduleTask();
    }
    ...

    public List<ProcessMetadata> getProcessMetadata() {
        return this.state.getProcessMetadata();
    }

    public List<String> getTopicMetadata() {
        return this.state.getTopicMetadata();
    }

    public List<ProcMetadata> getProcMetadata() {
        return this.state.getProcMetadata();
    }