Java 多线程 系列文章目录:
- Java 多线程(一)线程间的互斥和同步通信
- Java 多线程(二)同步线程分组问题
- Java 多线程(三)线程池入门 Callable 和 Future
- Java 多线程(四)ThreadPoolExecutor 线程池各参数的意义
- Java 多线程(五)Lock 和 Condition 实现线程同步通信
- Java 多线程(六)Semaphore 实现信号灯
- Java 多线程(七)CyclicBarrier 同步的工具类
- Java 多线程(八)CountDownLatch 同步工具类
- Java 多线程(九)Exchanger 同步工具类
- Java 多线程(十)ArrayBlockingQueue 阻塞队列
- Java 多线程(十一)JDK 同步集合
今天我们来介绍下,Java 中的阻塞队列:ArrayBlockingQueue
阻塞队列和非阻塞的区别:
如果队列里面已经放满了,如果是阻塞队列那么线程会一直阻塞,而非阻塞对垒则会抛出异常。
队列还包括固定长度的队列和不固定长度的队列。
ArrayBlockingQueue 类实现了 BlockingQueue 接口,该接口有如下方法:
拿 Insert 情况来说,如果队列里面已经满了,使用 add 方法往里放就会抛出异常。如果 offer 方法返回 false,也就是队列中没有可用空间, put 方法将会阻塞在那里,直到有空间可以放。
下面来看一个例子:
public class BlockingQueueTest {
public static void main(String[] args) {
//该队列里面只能放3个Integer
final BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(3);
for(int i=0;i<2;i++){
new Thread(){
public void run(){
while(true){
try {
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName() + "准备放数据!");
queue.put(1);//如果队列是满的,该方法就会阻塞
System.out.println(Thread.currentThread().getName() + "已经放了数据," +
"队列目前有" + queue.size() + "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
new Thread(){
public void run(){
while(true){
try {
//将此处的睡眠时间分别改为100和1000,观察运行结果
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "准备取数据!");
queue.take();//如果没有了数据,就会阻塞
System.out.println(Thread.currentThread().getName() + "已经取走数据," +
"队列目前有" + queue.size() + "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
运行结果如图所示:
我们可以通过使用两个阻塞队列,容量都是 1 的 ArrayBlockingQueue 来实现同步通知的功能,如:
static class Business {
BlockingQueue<Integer> queue1 = new ArrayBlockingQueue<Integer>(1);
BlockingQueue<Integer> queue2 = new ArrayBlockingQueue<Integer>(1);
//设置匿名构造方法,它会在实例化对象前执行.
{
try {
//因为要让主线程先执行.
queue2.put(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//不能加上synchronized,可能会导致死锁,put方法会阻塞但是不会释放锁
public void sub(int k){
try {
queue2.put(1);//第一次执行的时候因为已经满了,就阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 1; i <= 10; i++) {
System.out.println("sub thread sequence " + i + " loop of " + k);
}
try {
queue1.take();//这样queue1就可以put了
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//不能加上synchronized,可能会导致死锁,put方法会阻塞但是不会释放锁
public void main(int k) {
try {
queue1.put(1);//可以放
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 1; i <= 100; i++) {
System.out.println("main thread sequence " + i + " loop of " + k);
}
try {
queue2.take();//这样queue2就可以put了
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这样也能实现主线程和子线程之间的打印切换.