上周,我们有几个系统发生了线程死锁,导致系统的请求被挂住,无法响应请求。后面查了一下该问题,原来是我厂一个基础组件中使用的锁对象不一致而导致了死锁。
public class SimpleStore {
private Map sessions = Collections.synchronizedMap(new HashMap());
synchronized public void remove(String sessionID) { //A1
sessions.put(sessionID, ""); //A2
sessions.remove(sessionID);
System.out.println("remove " + sessionID);
}
public void commit(Map attrs, String sessionID, StatusHolder statusHolder) {
System.out.println("commit " + sessionID);
synchronized (sessions) { //B1
remove(sessionID); // B2
}
}
}
上面代码中:
private Map sessions = Collections.synchronizedMap(new HashMap());
将sessions这个map申明为线程安全的map,则操作map中的任何方法时,都会加锁,并且会锁住sessions对象。 这行代码,则在外部线程访问remove方法时会锁住SimpleStore这个对象。
synchronized public void remove(String sessionID);
可以看到,目前在同一个类或者方法中,有两把锁,并且锁对象不是同一个,那下面我们看看线程是怎么被死锁住的:
1, 假设A线程先调用remove方法,则这时会把simpleStore给锁住,然后执行sessions.put(sessionID, “”)的时候,会尝试锁住sessions
2, 同时B线程调用commit方法,在 synchronized (sessions) 时,会先锁住sessions对象,并且在调用接下来的remove()试,会尝试锁住 SimpleStore对象,至此,线程A和线程B终于成功完成死锁。
所以在使用多线程时一定要特别注意,使用锁一定要注意你的锁对象是否一致。要不然就有可能死锁了~