发现一个极佳的学习队列、生产者-消费者模式、多线程、同步(当然也有模板)的极佳的例子。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 数组阻塞队列
*
* @param <E>
*/
class ArrayBlockingQueueDemon<E> {
final ReentrantLock lock;
/**
* put元素时被阻塞的条件
*/
final Condition putCondition;
/**
* take数据时被阻塞的条件
*/
final Condition takeCondition;
/**
* 放元素的队列
*/
final Object[] items;
/**
* take下一个元素的索引下标
*/
int takeIndex;
/**
* put下一个元素的索引下标
*/
int putIndex;
/**
* 队列中元素个数
*/
int count;
/**
* 构造方法
*
* @param capacity 允许队列
* @param fair 是否创建公平锁
*/
public ArrayBlockingQueueDemon(int capacity, boolean fair) {
if(capacity <= 0) {
throw new IllegalArgumentException();
}
this.items = new Object[capacity];
lock = new ReentrantLock(fair); //公平锁和不公平锁
takeCondition = lock.newCondition();
putCondition = lock.newCondition();
}
public void put(E e) throws InterruptedException {
if(e == null) {
throw new NullPointerException();
}
lock.lockInterruptibly();
try {
// 队列到达初始化上限时,不再允许向队列放数据,放数据的线程要等待
// 此刻,当前线程会被添加到putCondition的阻塞队列里
while (count == items.length) {
putCondition.await();
}
// 入队
enqueue(e);
} finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
lock.lockInterruptibly();
try {
// 当队列没有数据时,拿数据的线程等待
// 此该,当前线程会被添加到takeCondition的阻塞队列里
while (count == 0) {
takeCondition.await();
}
// 出队并返回
return dequeue();
} finally {
lock.unlock();
}
}
private void enqueue(E x) {
items[putIndex] = x;
++putIndex;
if(putIndex == items.length) {
putIndex = 0;
}
count++;
// 队列里增加元素了,可以唤醒取元素的线程了
takeCondition.signal();
}
private E dequeue() {
E x = (E) items[takeIndex];
items[takeIndex] = null;
++takeIndex;
if(takeIndex == items.length) {
takeIndex = 0;
}
count--;
// 队列元素减少了,腾出了位置,可唤醒放元素的线程了
putCondition.signal();
return x;
}
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueueDemon arrayBlockingQueueDemon = new ArrayBlockingQueueDemon(100, true); //
for(int i = 0; i < 10; i++) {
int finalI = i;
new Thread(() -> {
try {
// Thread.sleep(1);
Integer integer = Integer.valueOf(finalI);
System.out.println(Thread.currentThread().getName() + " " + finalI + " running...");
arrayBlockingQueueDemon.put(integer);
} catch (Exception e) {
e.printStackTrace();
}
}, "入队").start();
}
for(int i = 0; i < 10; i++) {
int finalI = i;
new Thread(() -> {
try {
// Thread.sleep(1);
System.out.println(Thread.currentThread().getName() + " " + finalI + " running...");
System.out.println(arrayBlockingQueueDemon.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "出队").start();
}
Thread.sleep(5000);
}
}
这几个样例可以学的知识点包括:
1)队列:先进先出的表结构;
2)生产者-消费者模式:通过缓冲作为中间件,生产者发送数据,消费者读取数据;缓冲一般用队列。
3)多线程:线程是计算机中执行任务的最小单位(进程是拥有独立资源的最小单位)。执行线程获得CPU;线程阻塞会强占CPU;线程sleep或停止会退出CPU。多线程之间会进行任务切换,这牵涉对进程的管理,以及对CPU资源的占用。
4)同步:多线程之间同步,就是让任务取得一致。多线程同步,可以获得任务的一致性。多线程同步有多种机制,信号、临界区都是典型的机制。
当然,此样例还提供了泛型编程。
最后,对原代码出处表示致谢(Java锁详解之ReentrantLock_bug师姐的博客-CSDN博客)