多线程—阻塞队列的练习
想要实现的效果:对于一个动态数组,添加两个线程,一个线程负责添加数据,一个线程负责移除数据,最终使得该动态数组动态平衡保持在20这个长度
要求:
添加数据:如果满了就阻塞等待–也就是说当达到20这个大小的时候,再加会加不进去。线程会陷入堵塞,只有等到大小达到20以下的时候才会执行add()操作
移除数据:如果空了就阻塞等待–也就是说当达到0这个大小的时候,再减会减不下去。线程会陷入堵塞,只有等到大小达到0以上的时候才会执行remove()操作
这里就先使用add()/remove()方法
表现第一点:先加到20,加到20之后,输出堵塞,然后等到减了一个元素之后,再运行添加操作并输出
表现第二点:先减到0,输出”堵塞“,然后等到加了一个元素之后,再运行删除操作并输出
(一)编写一个Main类:
package Test.MyArrayBlockinigQueue;import java.util.ArrayList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Main {static ArrayList<Integer> arrBQ = new ArrayList<>();static Lock lock = new ReentrantLock();static Condition con = lock.newCondition();public static void main(String[] args) {AddTh add = new AddTh();RemoveTh remove = new RemoveTh();Thread addTh = new Thread(add);Thread removeTh = new Thread(remove);//演示减的操作就先增加到20,然后把增加操作延时一会,先运行移除操作for(int i = 0; i < 20 ; i ++){arrBQ.add(1);}//为什么当addTh里面每次循环如果不睡一会,就会在输出平台中出现断断续续的字迹try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}removeTh.start();addTh.start();}
}
(二)编写一个添加的线程类:
package Test.MyArrayBlockinigQueue;public class AddTh implements Runnable{@Overridepublic void run() {//添加数据直到长度为20之后堵塞while(true){try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}Main.lock.lock();try {Main.arrBQ.add(1);//长度大于等于20的时候陷入阻塞if (Main.arrBQ.size() >= 20) {System.out.println("要增,但此时长度为:" + Main.arrBQ.size() + ",陷入阻塞");Main.con.await();//长度小于20的时候继续加}else{System.out.println("要增,因为此时长度为:" + Main.arrBQ.size() + ",不再堵塞");Main.con.signal();}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {Main.lock.unlock();}}}
}
(三)编写一个删除的线程类:
package Test.MyArrayBlockinigQueue;public class RemoveTh implements Runnable{@Overridepublic void run() {//添加数据直到长度为20之后堵塞while(true){//演示“增加”的阻塞就把减的操作延后一会
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }Main.lock.lock();try {Main.arrBQ.removeLast();//长度=0的时候陷入阻塞if (Main.arrBQ.isEmpty()) {System.out.println("要减,但此时长度为:" + Main.arrBQ.size() + ",陷入阻塞");Main.con.await();//长度大于0的时候继续减}else{System.out.println("要减,因为此时长度为:" + Main.arrBQ.size() + ",所以不再堵塞");Main.con.signal();}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {Main.lock.unlock();}}}
}
注意:
1.如果要展示添加这个操作的阻塞情况,那么就需要把减的操作延后,因此需要在减的线程中设立睡眠时间,并且把加线程和主线程的睡眠和添加操作注释
2.如果要展示添加这个操作的阻塞情况,那么就需要在主函数中提前把数组加好,然后在加的线程中设立睡眠时间,并把减线程中的睡眠操作注释
实现效果:
1.添加阻塞:
2.删除阻塞:
——————————————————————————————————————————————————————————————————————————————————————————————————————(√)
进阶效果:创建一个仿ArrayBlockingQueue的动态数组,本身的put()/take()方法就具备了这样的特性
要求:
添加数据:如果满了就阻塞等待
移除数据:如果空了就阻塞等待
按照要求编写一个阻塞数组:
package Test.MyArrayBlockinigQueue;import com.sun.security.jgss.GSSUtil;import java.util.ArrayList;public class MyArrayBlockingQueue<E> {int length;ArrayList<E> arrBQ = new ArrayList<>();public MyArrayBlockingQueue(int length){this.length = length;}public void add(E i){Main.lock.lock();try {if (arrBQ.size() < length) {arrBQ.add(i);System.out.println("此时不满足20,可以加");Main.con.signal();} else {System.out.println("数组已满20");Main.con.await();}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {Main.lock.unlock();}}public void removeLast(){Main.lock.lock();try{//回去进一步思考逻辑if(arrBQ.isEmpty()) {System.out.println("删不下去了,因为此时长度为0");Main.con.await();}else {System.out.println("可以删,因为此时有数据");arrBQ.removeLast();Main.con.signal();}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {Main.lock.unlock();}}
}
(1)添加数据的阻塞情况的代码:
此时编写一个测试类:
package Test.MyArrayBlockinigQueue;public class Test {public static void main(String[] args) {MyArrayBlockingQueue<Integer> myArrBQ = new MyArrayBlockingQueue<>();for(int i = 0 ; i < 30 ; i++){myArrBQ.add(i);}}
}
效果如下:
(2)删除数据的阻塞队列的代码:
在原测试类中修改成:
package Test.MyArrayBlockinigQueue;import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;public class Test {public static void main(String[] args) {MyArrayBlockingQueue<Integer> myArrBQ = new MyArrayBlockingQueue<>(20);for(int i = 0 ; i < 20 ; i++){myArrBQ.add(i);}for(int i = 0 ; i < 30 ; i++){myArrBQ.removeLast();}}
}
实现效果如下:
至此,一个简单的阻塞队列实现。