解决Java中线程中的原子性问题

时间:2016-01-12 15:06:18

标签: java multithreading atomicity

此问题与我已发布的以下问题一起

Preventing thread from duplicate processing in java

问题陈述:如何控制许多子线程之间共享的某些代码块只能访问一次。

场景:

现在,我有一个场景,多个线程共享一个缓冲区资源(一个普通的并发列表),它用一些值更新列表并在最后清除它。但是我希望这个操作能够在原子性的情况下执行。只有在第一个线程更新并清除列表后,第二个线程才应开始更新列表。如果操作不同步而第一个线程作业没有完成,那么可能会导致数据问题。

代码片段如下,我试图在线程中放置一个synchronized块。这有意义吗?它会起作用吗?同步块内的行应该一次只能被一个线程访问,而块本身就是一个新生成的线程。

public void processControlMessage(final Message message) {
    try {
        lock.lock();
        RDPWorkflowControlMessage rdpWorkflowControlMessage = unmarshallControlMessage(message);
        if (rdpWorkflowControlMessage != null) {
            final String workflowName = rdpWorkflowControlMessage.getWorkflowName();
            final RdpWorkflowControlMessageType controlMessageType = rdpWorkflowControlMessage.getControlMessage();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        if (message instanceof TextMessage) {
                            if (controlMessageType != null && controlMessageType == RdpWorkflowControlMessageType.REFRESH) {
                                if (!isWorkflowBeingProcessed(workflowName)) {
                                    log.info("Processing workflow control message for the workflow " + workflowName);
                                    addShutdownHook(workflowName);
                                    List<String> matchingValues = new ArrayList<String>();
                                    matchingValues.add(workflowName);
                                    ConcreteSetDAO tasksSetDAO = taskEventListener.getConcreteSetDAO();
                                    ConcreteSetDAO workflowSetDAO = workflowEventListener.getConcreteSetDAO();
                                    tasksSetDAO.deleteMatchingRecords(matchingValues);
                                    workflowSetDAO.deleteMatchingRecords(matchingValues);
                                    List<RDPWorkflowTask> allTasks = fetchNewWorkflowItems(workflowName);
                                    //Will the below line be accessed and executed only by one thread??
                                    updateAndClearTasksandWorkflowSet(allTasks);
                                    removeWorkflowNameFromProcessingMap(workflowName);
                                } else {
                                    log.info("RDA cache clean up is already in progress for the workflow " + workflowName);
                                }
                            }
                        }
                    } catch (Exception e) {
                        log.error("Error extracting item of type RDPWorkflowControlMessage from message " + message);
                    }
                }
            }).start();
        }
    } finally {
        lock.unlock();
    }
}

private boolean isWorkflowBeingProcessed(final String workflowName) {
    if (controlMessageStateMap.get(workflowName) == null) {
        synchronized (this) {
            if (controlMessageStateMap.get(workflowName) == null) {
                log.info("Adding an entry in to the processing map for the workflow " + workflowName);
                controlMessageStateMap.put(workflowName, true);
                return false;
            }
        }
    }
    return true;
}

private void addShutdownHook(final String workflowName) {
    Runtime.getRuntime().addShutdownHook(new Thread() {
        public void run() {
            removeWorkflowNameFromProcessingMap(workflowName);
        }
    });
    log.info("Shut Down Hook attached for the thread processing the workflow " + workflowName);
}

private RDPWorkflowControlMessage unmarshallControlMessage(Message message) {
    RDPWorkflowControlMessage rdpWorkflowControlMessage = null;
    try {
        TextMessage textMessage = (TextMessage) message;
        rdpWorkflowControlMessage = marshaller.unmarshalItem(textMessage.getText(), RDPWorkflowControlMessage.class);
    } catch (Exception e) {
        log.error("Error extracting item of type RDPWorkflowTask from message " + message);
    }
    return rdpWorkflowControlMessage;
}

private List<RDPWorkflowTask> fetchNewWorkflowItems(String workflowName) {
    workflowRestServiceClient.initSSL();
    List<RDPWorkflowTask> allTasks = workflowRestServiceClient.fetchWorkflowSpecificTasks(workflowName);
    return allTasks;
}

private void updateAndClearTasksandWorkflowSet(List<RDPWorkflowTask> allTasks) {
    synchronized (this) {
           if (allTasks != null && allTasks.size() > 0) {
                taskEventListener.addRDPWorkflowTasks(allTasks);
                workflowEventListener.updateWorkflowStatus(allTasks);
                taskEventListener.getRecordsForUpdate().clear();
                workflowEventListener.getRecordsForUpdate().clear();
            }
    }

}

private synchronized void removeWorkflowNameFromProcessingMap(String workflowName) {
    if (workflowName != null
            && (controlMessageStateMap.get(workflowName) != null && controlMessageStateMap.get(workflowName))) {
        controlMessageStateMap.remove(workflowName);
        log.info("Thread processing the " + workflowName + " is released from the status map");
    }
}

1 个答案:

答案 0 :(得分:0)

我认为你这是错误的做法。在processControlMessage()方法中,您将多次锁定和释放事物。您在某些情况下锁定实例,而在其他情况下,您正在同步方法。没有办法知道这些锁和版本之间发生了什么。

我相信您提交的内容,您正在尝试确保一次只有一个线程访问controlMessageStateMap。如果这是正确的,那么为什么不在块的开头锁定Map并在完成后释放它?

new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    synchronize(controlMessageStateMap) {
                    if (message instanceof TextMessage) {
                        if (controlMessageType != null && controlMessageType == RdpWorkflowControlMessageType.REFRESH) {
                            if (!isWorkflowBeingProcessed(workflowName)) {
                        ...
                    } // end of synchronize block
                } catch (Exception e) {
                    log.error("Error extracting item of type RDPWorkflowControlMessage from message " + message);
                }

我认为这不是一个很好的策略。但是如果不了解你想要做的事情的背景,我就不能建议另一种选择。

什么是原子性保护?你试图解决的用例是什么?