简体   繁体   中英

Synchronized Method might not be releasing lock

Today I encountered an issue that speculates that a method did not release a lock, causing a deadlock situation.

The environment:

This is a web app on tomcat and as users from the UI make calls to the server the threads enter a servlet. The servlet has a spring bean autowired where via a method in the servlet calls a method in the bean.

The method is synchronized and all it does is does a batch insert(Oracle DB) via spring jdbc template a List of String.(2 lines of code, 1 jdbcTemplate batch update and one log)

While testing my browser hanged and i closed it and hen no one was able to enter the synchronized method.

Is there something I might need to keep in mind in this situation when using synchronized in this environment? Does anyone have a speculation on what might have happened? How can I guarantee that the lock will be released after some time?

Thank you

public class LockManager  {    

private JdbcTemplate jdbcTemplate;

private static final Log LOG = LogFactory.getLog(LockManager.class);


/* synchronized to guarantee that only one thread at a time is inserting. */
public synchronized void lockTransactions(final List<String> idList, final String userID) throws MyException{

    try{
        final Timestamp occuranceTime = new Timestamp(System.currentTimeMillis());
        jdbcTemplate.batchUpdate(INSERT_ID, new LockInsertBatchPreparedStatementSetter(idList,userID, occuranceTime));
        LOG.info("Lock Rows completed. ID List size="+idList.size());
    }catch(Exception e){
        handleInsertException(e);
    }
}



public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;
}

/* Throw appropriate exception. */
private void handleInsertException(final Exception e) throws MyException{
    String message = e.getMessage();
    MyException me = null;
    if(message.contains("ORA-00001")){
       me= new MyException(ExceptionMessage.LOCK_CONSTRAINT.getMessage(),e);
    }else{
       me= new MyException(ExceptionMessage.LOCK_ERROR.getMessage(),e);
    }
    LOG.error("Exception while locking  IDs",me);
    throw me;
}


private class LockInsertBatchPreparedStatementSetter implements BatchPreparedStatementSetter{

    private List<String> idList;
    private String userID;
    private Timestamp occuranceTime;

    private  final Log LOG = LogFactory.getLog(LockInsertBatchPreparedStatementSetter.class);


    public LockInsertBatchPreparedStatementSetter(final List<String> idList, final String userID, final Timestamp occuranceTime){
        this.idList = idList;
        this.userID = userID;
        this.occuranceTime=occuranceTime;
    }

    @Override
    public int getBatchSize() {
        return idList.size();
    }

    @Override
    public void setValues(PreparedStatement ps, int i) throws SQLException {
        String thisID= idList.get(i);
        ps.setString(1, thisID);
        ps.setString(2, userID);
        ps.setTimestamp(3, occuranceTime);
        LOG.info("Attempting to Lock  ID:"+thisID+ " for user:"+userID);

    }
}

}

Firstly, you should make sure that your synchronized method contains code only what needs to be synchronized.

Secondly if you are doing an operation like DB write as you have mentioned(batch insert(Oracle DB)), make sure that you set timeouts on such operations. What if your batch insert is taking too long, may happen due to variety of reasons. A timeout should help to either finish the job quickly or simply exit without finishing it.

Any slow operation inside a synchronized method, will surely be a killer for other threads trying to enter that method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM