如何从数据存储区获取和设置属性的操作,线程安全?
目前,我有代码将任务放入队列,每个任务执行一项任务,然后更新一个名为numberOfTasks的属性,类型为int。它基本上获取此属性的当前值并递增它。
但是,由于任务在队列中执行,因此线程问题导致最终值不正确。有时,两个任务会尝试同时更新原则,因此有时候增量没有完成。
有人可以帮忙正确完成这项工作吗?
数据存储属性获取方法:
private String doGet(String rowId) throws EntityNotFoundException {
Key egsKey = KeyFactory.createKey(DATASTORE_KIND, rowId);
Entity egsEntity = datastore.get(egsKey);
// schema changed from String to Text type. Transparently handle that here.
Object propertyValue = egsEntity.getProperty(PROPERTY_KEY);
if (propertyValue instanceof String) {
return (String) propertyValue;
}
Text text = (Text) propertyValue;
return text.getValue();
}
数据存储属性设置方法:
private void doPut(String rowId, List<String> list) {
Entity entity = new Entity(DATASTORE_KIND, rowId);
entity.setProperty(PROPERTY_KEY, list);
datastore.put(entity);
}
Setter和Getter方法:
public synchronized int getPendingUsersForProcessing() {
String pendingUsersForProcessingAsString = null;
try {
pendingUsersForProcessingAsString = doGet(PENDING_USERS_FOR_PROCESSING);
return Integer.valueOf(pendingUsersForProcessingAsString);
} catch (NumberFormatException e) {
throw new IllegalStateException("The num of last batches processed in Datastore is not a number: "
+ pendingUsersForProcessingAsString);
} catch (EntityNotFoundException e) {
return DEFAULT_PENDING_USERS_FOR_PROCESSING;
}
}
/** {@inheritDoc } */
@Override
public synchronized void setPendingUsersForProcessing(int pendingUsersForProcessing) {
doPut(PENDING_USERS_FOR_PROCESSING, String.valueOf(pendingUsersForProcessing));
LOG.info("Number of Pending Users For Processing is set to : " + pendingUsersForProcessing);
}
代码我在尝试更新属性的地方:
int pendingUsers = appProperties.getPendingUsersForProcessing();
int requestUsers = request.getUserKeys().size();
appProperties.setPendingUsersForProcessing(pendingUsers + requestUsers);
答案 0 :(得分:1)
这不是一个线程问题,因为您的应用可能有多个实例执行任务,而这些实例彼此不了解。所以这是一个争用的情况。
您有多种方法可以解决它。
不是不断更新同一个实体,而是使用任务作为id完成的时间为每个已完成的任务创建一个新实体。这种方法的优点是它创建了一个审计跟踪,你可以随时获得统计数据,如今天完成的任务数量,在过去一小时内等。要计算实体数量,你可以使用仅限密钥的查询,这是几乎免费且非常快。缺点是编写这些实体的成本较高 - 如果您要完成大量任务,这不是解决方案。
不计算任务,而是计算这些任务的结果。例如,如果任务更新用户状态,则可以使用免费且快速的仅密钥查询来计算具有“待处理”状态的用户数。如果您已经拥有一个索引属性,可以将其用作计算已完成任务的标记,那么这是一种非常好的方法。