首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > JAVA > J2SE开发 >

编写了一个线程安全:(的阻塞队列,但却出现了死锁,请高手帮忙看看,小弟谢谢了

2012-01-31 
编写了一个线程安全:(的阻塞队列,但却出现了死锁,请高手帮忙看看,小弟多谢了/*编写一个线程安全、大小固定

编写了一个线程安全:(的阻塞队列,但却出现了死锁,请高手帮忙看看,小弟多谢了
/*
编写一个线程安全、大小固定的队列
提供阻塞式的方法put,若队列没有空间,则方法put会一直等待
提供阻塞式的方法take,若队列为空,则方法take会一直等待
启动30个线程操作该队列,每个线程进行一次put和一次take操作 
*/

/*
已经按照顺序获得读锁和写锁了,但是如果启动30个线程的话,基本上每次都会死锁,线程都停在read_lock.wait()的位置,
如果启动20个线程就只有一半的几率会死锁(其实都在等待read_lock的锁,不能说是死锁),但每一个线程take一次必然会put一次,
或者反过来,按说是不会有都等待read_lock的情况
*/

package com.huawei.test;

import java.util.*;

public class Queue1
{
  final int SIZE = 10; //队列固定大小
  ArrayList store = new ArrayList(SIZE);

  Object write_lock = new Object();//用于对store的写操作,如get/add/set/remove
  Object read_lock = new Object(); //用于对store只读操作,如取size

  public Queue1(){}

  public void put (Object o) //没有空间一直等待
  {
  while(true){
  synchronized(read_lock){
  try{
  if(store.size() == SIZE){
  read_lock.wait();//如果队列已满,就释放锁
  }else{
  synchronized(write_lock){
  Thread.sleep(50);
  store.add(o); //增加元素到队列
  System.out.println(Thread.currentThread().getName() + "****PUT::Size=" + store.size());
  Thread.sleep(50);
  read_lock.notifyAll(); //通知其他线程
  break;
  }
  }
  }catch(Exception ex){
  ex.printStackTrace(System.err);
  }
  }
  }
  }


  public Object take () //没有数据一直等待
  {
  while(true){
  synchronized(read_lock){
  try{
  if(store.size() == 0){
  read_lock.wait();//如果队列没有数据,就释放锁
  }else{
  synchronized(write_lock){
  Thread.sleep(50);
  Object obj = store.remove(0); //从队列头移走数据
  System.out.println(Thread.currentThread().getName() + "****Take::Size=" + store.size());
  Thread.sleep(50);
  read_lock.notifyAll();//通知其他线程
  return obj;
  }
  }
  }catch(Exception ex){
  ex.printStackTrace(System.err);
  }
  }
  }
  }


  public static void main(String[] args){
  Queue1 queue1 = new Queue1(); //创建一个队列

  for(int i = 0; i < 30; i++){ //启动30个线程访问队列
  TestThread thread = new TestThread(queue1,i);
  System.out.println( "--Thread:" + i + " Start!" );
  thread.start();
  try{
  Thread.sleep(10); //没隔十毫秒启动一个线程
  }catch(Exception ex){
  ex.printStackTrace(System.err);
  }
  }
  }

}


class TestThread extends Thread
{
  Queue1 queue1 = null;
  int sn = 0;

  public TestThread(Queue1 queue1,int sn){
  this.queue1 = queue1;
  this.sn = sn;
  setName("Thread::" + sn); //以序号作为线程名
  }

  public void run(){
  String tmp = null;


  try{
  if( sn < 7){ //sn小于7的线程先put,后take
  tmp = "Thread-PUT::" + sn + "---put::";
  queue1.put(tmp);
  Thread.sleep(10);
  tmp = "Thread-Take::" + sn + "---take::";
  Object obj = queue1.take();
  }else{ //sn大于7的线程先take,后put
  tmp = "Thread-Take::" + sn + "---take::";
  Object obj = queue1.take();
  Thread.sleep(10);
  tmp = "Thread-PUT::" + sn + "---put::";
  queue1.put(tmp);
  }
  System.out.println("Thread::" + sn + " task over!");
  }catch(Exception ex){
  ex.printStackTrace(System.err);
  }

  }
}


[解决办法]
只有消费者,而没有生产者的问题了.
你的sn小于7的线程先存后取,而大于7的线程先取后存.而且都只执行一次。
那你想过没有,在sn小于7的线程全部都执行完了以后呢??
也就是说都是在执行大于7的线程的时候呢?他们去取,取不到,好,我等,等谁放啊?没有人放啊,都在等着取啊.

改进有几种办法,一是平衡存取线程数量:
 if( sn < 7)
改为 if(sn%2==0)

还有:判断是否还有线程在存,没有了的话,将先取线程的操作改为先存后取.
[解决办法]
楼上说的及是了...
[解决办法]
你说你已经按照顺序获得读锁和写锁了,但是你根本就没有明白你写的代码,你并没有实现顺序获得读锁和解锁,你是启动了30个线程,每一个线程的执行顺序,并不是按顺序的,晚启动的线程可能比早启动的线程还要调用得早,
关于你程序的死锁我认为是从你的第7个线程开始,就先执行take再执行put,当store中为空,有可能所有的没有执行完的线程执行都在执行take,这时所有线程的read_lock都被锁住,也就没有线程去put,这样store永远为空,所有剩余线程都在等待读,但又没有线程去写。这个程序会不会死锁,是一种偶然性,就是启动一百个线程,只有第一个是先put后take,也有可能不死锁,第一个线程put完,第二个去take,第二个put,第三个take,.....直到100个put完,一个线程take,但是这种概率可想而知有多小。
楼上说的在理,但不全对,但是并不是在执行大于7的线程时就一定会死锁,有可能第一个线程启动后,执行完put后,take就一直不执行,对上面的代码的System.out.println();我作了修改,看看下面就知道了(这是其中的一部分)
第0 个线程开始启动!
队列没有满,Thread::0开始存入队列****队列个数=1
第1 个线程开始启动!
队列没有满,Thread::1开始存入队列****队列个数=2
第2 个线程开始启动!
Thread::0开始取
第3 个线程开始启动!
Thread::0取走了:第0个线程存放的数据****队列中的个数=1
第4 个线程开始启动!
第0个线程 存放的数据 取走!
队列没有满,Thread::2开始存入队列****队列个数=2
第5 个线程开始启动!
队列没有满,Thread::5开始存入队列****队列个数=3
第6 个线程开始启动!
队列没有满,Thread::4开始存入队列****队列个数=4
Thread::1开始取
第7 个线程开始启动!
Thread::1取走了:第1个线程存放的数据****队列中的个数=3
第8 个线程开始启动!
第1个线程 存放的数据 取走!
第9 个线程开始启动!
队列没有满,Thread::3开始存入队列****队列个数=4
第10 个线程开始启动!
Thread::10开始取
第11 个线程开始启动!
Thread::10取走了:第2个线程存放的数据****队列中的个数=3
第12 个线程开始启动!
第2个线程 取走!
Thread::9开始取
第13 个线程开始启动!
Thread::9取走了:第5个线程存放的数据****队列中的个数=2
第5个线程 取走!
Thread::8开始取
第14 个线程开始启动!
Thread::8取走了:第4个线程存放的数据****队列中的个数=1
第15 个线程开始启动!
第4个线程 取走!
Thread::4开始取
第16 个线程开始启动!
Thread::4取走了:第3个线程存放的数据****队列中的个数=0
第17 个线程开始启动!
Thread::7开始取,没有数据
第3个线程 存放的数据 取走!
Thread::5开始取,没有数据
第18 个线程开始启动!
队列没有满,Thread::6开始存入队列****队列个数=1
Thread::2开始取
第19 个线程开始启动!
Thread::2取走了:第6个线程存放的数据****队列中的个数=0
第20 个线程开始启动!
第6个线程 存放的数据 取走!
Thread::20开始取,没有数据
Thread::6开始取,没有数据
Thread::19开始取,没有数据
Thread::5开始取,没有数据
Thread::7开始取,没有数据
Thread::18开始取,没有数据
Thread::17开始取,没有数据
第21 个线程开始启动!
队列没有满,Thread::8开始存入队列****队列个数=1

热点排行