为什么这样使用信号量会死锁?

时间:2021-05-13 15:15:11
有一个数据类,里面有一些,读的时候可以多个读线程,但读写需要互斥访问。我把资源的互斥访问代码封装在数据类里,各个读写线程内不做资源互斥访问处理。数据类的概要代码如下:

public class Data 
{
         //存放消息的hash表
private static Map<Long,Message> messageList = new HashMap<Long, Message>();

         //互斥访问量
public static final Semaphore read = new Semaphore(1);
public static final Semaphore write = new Semaphore(1);
public static int readerCount = 0;

         private static final Data data = new Data();//单例

private Data()
{
}

public static Data getInstance()
{
return data;
}


         //添加一个消息到消息表中
public void addMessage(Message msg)
{
Long key = msg.getId();
writeAcquire();
messageList.put(key, msg);
writeRelease();
}

         //移除某个消息
public void removeMessage(Long key)
{
writeAcquire();
messageList.remove(key);
writeRelease();
}

         /*          
          *还有很多方法,这里省略
          */

         //读者信号加锁
public void readAcquire()
{
try 
{
read.acquire();

readerCount++;
if(readerCount == 1)
{
write.acquire();
}
read.release();

catch(InterruptedException e)
{
e.printStackTrace();
}
}

//写者信号加锁
public void writeAcquire()
{
try 
{
write.acquire();

catch(InterruptedException e)
{
e.printStackTrace();
}
}

//读者信号解锁
public void readRelease()
{
try 
{
read.acquire();
readerCount--;
if(readerCount == 0)
{
write.release();
}
read.release();

catch(InterruptedException e)
{
e.printStackTrace();
}
}

//写者信号解锁
public void writeRelease()
{
write.release();
}
}


但是这样会死锁,短则一两分钟,多则10来分钟,线程就全部卡死了。多线程的调试实在是让人崩溃,大牛们看看问题在哪。

5 个解决方案

#1


好像是出错在这儿:public void readAcquire()
    {
        try 
        {
            read.acquire();
            
            readerCount++;
            if(readerCount == 1)
            {
                write.acquire();
            }
            read.release();
        } 
        catch(InterruptedException e)
        {
            e.printStackTrace();
        }
    }
write.acquire();没有释放信号量啊,这一段还有其他的错误。写者write就始终申请不到呀!
另外读者的初始信号量应设置成多给人进去读,还有就是这个acquier(),和release()方法要某一个出现的后面配对样。

#2


你的测试代码是什么?

#3


建议使用 ReentrantReadWriteLock,有现成的读写锁为啥不用?

如果要自己实现一个读写锁的话是很麻烦的。

#4


引用 1 楼 wuda236556254 的回复:
好像是出错在这儿:public void readAcquire()
  {
  try 
  {
  read.acquire();
   
  readerCount++;
  if(readerCount == 1)
  {
  write.acquire();
  }
  read.release();
  } 
  catch(InterruptedExcept……


写者信号的解锁是在readerRelease函数里,当读者退出时会调用readerRelease函数。

#5


引用 2 楼 bao110908 的回复:
你的测试代码是什么?


测试代码就是一些线程不断的对data类里的messageList进行操作,读或写。如Data.getInstance.addMessage  Data.getInstance.removeMessage或getMessage。线程内部没有互斥访问的代码,互斥访问的代码嵌在data的各个方法里。

#1


好像是出错在这儿:public void readAcquire()
    {
        try 
        {
            read.acquire();
            
            readerCount++;
            if(readerCount == 1)
            {
                write.acquire();
            }
            read.release();
        } 
        catch(InterruptedException e)
        {
            e.printStackTrace();
        }
    }
write.acquire();没有释放信号量啊,这一段还有其他的错误。写者write就始终申请不到呀!
另外读者的初始信号量应设置成多给人进去读,还有就是这个acquier(),和release()方法要某一个出现的后面配对样。

#2


你的测试代码是什么?

#3


建议使用 ReentrantReadWriteLock,有现成的读写锁为啥不用?

如果要自己实现一个读写锁的话是很麻烦的。

#4


引用 1 楼 wuda236556254 的回复:
好像是出错在这儿:public void readAcquire()
  {
  try 
  {
  read.acquire();
   
  readerCount++;
  if(readerCount == 1)
  {
  write.acquire();
  }
  read.release();
  } 
  catch(InterruptedExcept……


写者信号的解锁是在readerRelease函数里,当读者退出时会调用readerRelease函数。

#5


引用 2 楼 bao110908 的回复:
你的测试代码是什么?


测试代码就是一些线程不断的对data类里的messageList进行操作,读或写。如Data.getInstance.addMessage  Data.getInstance.removeMessage或getMessage。线程内部没有互斥访问的代码,互斥访问的代码嵌在data的各个方法里。