/*
编写一个线程安全、大小固定的队列
提供阻塞式的方法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);
} }
}
编写一个线程安全、大小固定的队列
提供阻塞式的方法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)还有:判断是否还有线程在存,没有了的话,将先取线程的操作改为先存后取.
你定义一个static int a = 0;
然后在take方法中加入如下的打印语句,就能看出来,你的取线程正在忙碌的进进出出了.
if(store.size() == 0){ System.out.println("我正在等待"+a);
a++;
read_lock.wait();//如果队列没有数据,就释放锁
关于你程序的死锁我认为是从你的第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
以前也十分怀疑是这种原因,可惜走思维胡同了,死活没想出来:(,多谢指教^_^而且其实后面的write_lock丝毫没有用,take和put函数都要获得read_lock锁才能执行,而且这个队列始终只有一个线程在读或者写,效率太差了。
但是size是读写都会影响的数据,感觉只有把它作为锁才能保证线程安全。有没有一个更高效写法,来满足这个队列的要求??? 请教!