我在代码中添加了注释,以解释发生死锁的位置。
基本上,有两个线程。每个线程获取Manager
对象上的锁,然后获取对静态资源的锁定,静态资源是应用程序中所有Manager
个对象的映射。两个线程在地图上调用get()
。
Manager
类已覆盖equals()
方法。 equals()
进一步调用Manager
类的一些同步方法。因此,地图上的get()
将逐一需要对地图中的每个对象进行对象级锁定,直到关键匹配,因为等于被覆盖。
我只能更改子类(Sub1
和Sub2
)中的代码并避免死锁,因为我无法访问其他类。
编辑:我无法访问syncMap。 “synchronized”块中的代码在我调用其API的第三方代码中执行。
我可以通过finally
Manager
获取锁定而不是在尝试阻止之前避免这种情况吗?!
public class Parent{
protected Manager manager;
}
public class Global{
private static final Map syncMap = Collections.synchronizedMap(new HashMap());
//syncMap contains all the objects of Manager in the application
}
class Manager{
public boolean equals(Object o){
Manager obj = (Manager)o;
return obj.getURL().equals(getURL());
}
public final synchronized String getURL(){
return msettings.getDBURL(); //msettings is a global variable
}
}
//Thread-1 is executing someMethod() of this class
class Sub1 extends Parent{
Global global;
//consider manager and Global object are not null
public void someMethod()
{
synchronized(manager){// Thread-1 succesfully takes object level lock on a manager object, say Manager01
try{
global.syncMap.get(manager);
// Thread-1 Succesfully takes class level lock on syncMap
// get() calls equals() for each object in syncMap.
//equals() need object lock on each Manager Object in map as it further calls synchronized getURL()
// But on one manager Object(Manager02) Thread-2 has already acquired lock and is waiting for lock on syncMap which this thread-1 holds
}
finally{
manager.releaseConnection();
}
}
}
}
//Thread-2 is executing otherMethod() of this class
class Sub2 extends Parent{
public void otherMethod()
{
synchronized(manager){// this takes a lock on manager(Manager02)
try{
global.syncMap.get(manager);
// this is blocked as syncMap is aquired by thread-1
}
finally{
manager.releaseConnection();
}
}
}
}
答案 0 :(得分:1)
首先,您真的应该尝试在equals
方法中消除同步的需要。它会造成比它解决的更多麻烦,所以如果可以进行重新设计,那么我认为这是最好的方法。
但是,如果稍微重构代码并将global.syncMap.get(manager)
移到synchronization
块之前,则不会产生死锁
public Class Parent{
protected Manager manager;
}
class Global{
private static final Map syncMap = Collections.synchronizedMap(new HashMap());
//syncMap contains all the objects of Manager in the application
}
class Manager{
public boolean equals(Object o){
Manager obj = (Manager)o;
return obj.getURL().equals(getURL());
}
public final synchronized String getURL(){
return msettings.getDBURL(); //msettings is a global variable
}
}
//Thread-1 is executing someMethod() of this class
class Sub1 extends Parent{
Global global;
//consider manager and Global object are not null
public void someMethod()
{
try {
global.syncMap.get(manager);
synchronized(manager){
}
}
finally{
manager.releaseConnection();
}
}
}
//Thread-2 is executing otherMethod() of this class
class Sub2 extends Parent{
public void otherMethod()
{
try {
global.syncMap.get(manager);
synchronized(manager){
}
}
finally{
manager.releaseConnection();
}
}
}
更新 Global.class
上的替代同步,可能也使用实例变量global
而不是Global.class
更新已将同步更改为Manager.class
而不是Global.class
。
class Sub1 extends Parent
{
Global global;
public void someMethod()
{
synchronized (Manager.class) {
try {
global.syncMap.get(manager);
}
finally {
manager.releaseConnection();
}
}
}
}
class Sub2 extends Parent
{
Global global;
public void otherMethod()
{
synchronized (Manager.class) {
try {
global.syncMap.get(manager);
}
finally {
manager.releaseConnection();
}
}
}
}
答案 1 :(得分:1)
在获得新信息之后,除了将所有处理转换为串行样式之外,我没有看到其他解决方案。因此,您可以将所有与管理器相关的API调用放在某个包装类的一个同步方法中,并将此包装器用作第三方API的单个入口点。
class BrutalWrapper {
public synchronized void doIt(Manager manager)
{
try{
global.syncMap.get(manager);
}
finally{
manager.releaseConnection();
}
}
}
class Sub1 extends Parent{
BrutalWrapper brutal;
public void someMethod()
{
brutal.doIt(manager);
}
}
class Sub2 extends Parent{
BrutalWrapper brutal;
public void someMethod()
{
brutal.doIt(manager);
}
}