#vulnerable code 
                                @Override
    public void approve(CoinData coinData, Transaction tx) throws NulsException {
        //spent the transaction specified output in the cache when the newly received transaction is approved.
        UtxoData utxoData = (UtxoData) coinData;
        for (UtxoInput input : utxoData.getInputs()) {
            input.setTxHash(tx.getHash());
        }
        for (UtxoOutput output : utxoData.getOutputs()) {
            output.setTxHash(tx.getHash());
        }

        List<UtxoOutput> unSpends = new ArrayList<>();
        Set<String> addressSet = new HashSet<>();
        try {
            //update inputs referenced utxo status
            for (int i = 0; i < utxoData.getInputs().size(); i++) {
                UtxoInput input = utxoData.getInputs().get(i);
                UtxoOutput unSpend = ledgerCacheService.getUtxo(input.getKey());
                if (null == unSpend) {
                    throw new NulsRuntimeException(ErrorCode.DATA_ERROR, "the output is not exist!");
                }
                if (!unSpend.isUsable()) {
                    throw new NulsRuntimeException(ErrorCode.UTXO_UNUSABLE);
                }
                if (OutPutStatusEnum.UTXO_CONFIRM_UNSPEND == unSpend.getStatus()) {
                    unSpend.setStatus(OutPutStatusEnum.UTXO_CONFIRM_SPEND);
                } else if (OutPutStatusEnum.UTXO_UNCONFIRM_UNSPEND == unSpend.getStatus()) {
                    unSpend.setStatus(OutPutStatusEnum.UTXO_UNCONFIRM_SPEND);
                }
                unSpends.add(unSpend);
                addressSet.add(unSpend.getAddress());
            }

            //cache new utxo ,it is unConfirm
            approveProcessOutput(utxoData.getOutputs(), tx, addressSet);
        } catch (Exception e) {
            //rollback
            for (UtxoOutput output : unSpends) {
                if (OutPutStatusEnum.UTXO_CONFIRM_SPEND.equals(output.getStatus())) {
                    ledgerCacheService.updateUtxoStatus(output.getKey(), OutPutStatusEnum.UTXO_CONFIRM_UNSPEND, OutPutStatusEnum.UTXO_CONFIRM_SPEND);
                } else if (OutPutStatusEnum.UTXO_UNCONFIRM_SPEND.equals(output.getStatus())) {
                    ledgerCacheService.updateUtxoStatus(output.getKey(), OutPutStatusEnum.UTXO_UNCONFIRM_UNSPEND, OutPutStatusEnum.UTXO_UNCONFIRM_SPEND);
                }
            }
            // remove cache new utxo
            for (int i = 0; i < utxoData.getOutputs().size(); i++) {
                UtxoOutput output = utxoData.getOutputs().get(i);
                ledgerCacheService.removeUtxo(output.getKey());
            }
            throw e;
        } finally {
            //calc balance
            for (String address : addressSet) {
                UtxoTransactionTool.getInstance().calcBalance(address, false);
            }
        }
    } 
                            #location 35 
                            #vulnerability type THREAD_SAFETY_VIOLATION
#fixed code
                                @Override
    public void approve(CoinData coinData, Transaction tx) throws NulsException {
        //spent the transaction specified output in the cache when the newly received transaction is approved.
        UtxoData utxoData = (UtxoData) coinData;
        for (UtxoInput input : utxoData.getInputs()) {
            input.setTxHash(tx.getHash());
        }
        for (UtxoOutput output : utxoData.getOutputs()) {
            output.setTxHash(tx.getHash());
        }

        List<UtxoOutput> unSpends = new ArrayList<>();
        Set<String> addressSet = new HashSet<>();
        try {
            lock.lock();
            //update inputs referenced utxo status
            for (int i = 0; i < utxoData.getInputs().size(); i++) {
                UtxoInput input = utxoData.getInputs().get(i);
                UtxoOutput unSpend = ledgerCacheService.getUtxo(input.getKey());
                if (null == unSpend) {
                    throw new NulsRuntimeException(ErrorCode.DATA_ERROR, "the output is not exist!");
                }
                if (!unSpend.isUsable()) {
                    throw new NulsRuntimeException(ErrorCode.UTXO_UNUSABLE);
                }
                if (OutPutStatusEnum.UTXO_CONFIRM_UNSPEND == unSpend.getStatus()) {
                    unSpend.setStatus(OutPutStatusEnum.UTXO_CONFIRM_SPEND);
                } else if (OutPutStatusEnum.UTXO_UNCONFIRM_UNSPEND == unSpend.getStatus()) {
                    unSpend.setStatus(OutPutStatusEnum.UTXO_UNCONFIRM_SPEND);
                }
                unSpends.add(unSpend);
                addressSet.add(unSpend.getAddress());
            }

            //cache new utxo ,it is unConfirm
            approveProcessOutput(utxoData.getOutputs(), tx, addressSet);
        } catch (Exception e) {
            //rollback
            for (UtxoOutput output : unSpends) {
                if (OutPutStatusEnum.UTXO_CONFIRM_SPEND.equals(output.getStatus())) {
                    ledgerCacheService.updateUtxoStatus(output.getKey(), OutPutStatusEnum.UTXO_CONFIRM_UNSPEND, OutPutStatusEnum.UTXO_CONFIRM_SPEND);
                } else if (OutPutStatusEnum.UTXO_UNCONFIRM_SPEND.equals(output.getStatus())) {
                    ledgerCacheService.updateUtxoStatus(output.getKey(), OutPutStatusEnum.UTXO_UNCONFIRM_UNSPEND, OutPutStatusEnum.UTXO_UNCONFIRM_SPEND);
                }
            }
            // remove cache new utxo
            for (int i = 0; i < utxoData.getOutputs().size(); i++) {
                UtxoOutput output = utxoData.getOutputs().get(i);
                ledgerCacheService.removeUtxo(output.getKey());
            }
            throw e;
        } finally {
            lock.unlock();
            //calc balance
            for (String address : addressSet) {
                UtxoTransactionTool.getInstance().calcBalance(address, false);
            }
        }
    }