/*
编写一个线程安全、大小固定的队列
提供阻塞式的方法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);
        }    }
}

解决方案 »

  1.   

    只有消费者,而没有生产者的问题了.
    你的sn小于7的线程先存后取,而大于7的线程先取后存.而且都只执行一次。
    那你想过没有,在sn小于7的线程全部都执行完了以后呢??
    也就是说都是在执行大于7的线程的时候呢?他们去取,取不到,好,我等,等谁放啊?没有人放啊,都在等着取啊.改进有几种办法,一是平衡存取线程数量:
     if( sn  < 7)
    改为  if(sn%2==0)还有:判断是否还有线程在存,没有了的话,将先取线程的操作改为先存后取.
      

  2.   

    忘记说了了,你的线程并没有死锁.
    你定义一个static int a = 0;
    然后在take方法中加入如下的打印语句,就能看出来,你的取线程正在忙碌的进进出出了.
    if(store.size() == 0){ System.out.println("我正在等待"+a);
    a++;

    read_lock.wait();//如果队列没有数据,就释放锁 
      

  3.   

    你说你已经按照顺序获得读锁和写锁了,但是你根本就没有明白你写的代码,你并没有实现顺序获得读锁和解锁,你是启动了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
      

  4.   

    多谢上面的大侠,前七个都是先写后读,那么在某一个状态,可能会出现队列为空,而后面的进程都是先读后写的,等待生产者的状态。确实不是死锁,队列本身其实是正确的,只是读写队列的操作者出了问题。在僵住的状态下,其实只要加入一个先写后读的进程,就解开了僵局,可惜后面再也没有生产了。
    以前也十分怀疑是这种原因,可惜走思维胡同了,死活没想出来:(,多谢指教^_^而且其实后面的write_lock丝毫没有用,take和put函数都要获得read_lock锁才能执行,而且这个队列始终只有一个线程在读或者写,效率太差了。
    但是size是读写都会影响的数据,感觉只有把它作为锁才能保证线程安全。有没有一个更高效写法,来满足这个队列的要求??? 请教!