目录
题目:一个初始值为零的变量,两个线程对其交替操作,一个加1一个减1
题目:线程通信之顺序调用 ReentrantLock,实现A->B->C三个线程顺序执行
题目:线程通信之生产者消费者阻塞 BlockingQueue队列版
阻塞队列
首先它是一个队列,而一个阻塞队列在数据结构中所起的作用大致如下图:
当阻塞队列是空时,从队列中获取元素的操作将会被阻塞。
当阻塞队列是满时,往队列中添加元素的操作将会被阻塞。
在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒。
为什么需要BlockingQueue
好处是:我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办了。
在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。
阻塞队列的核心方法:
抛出异常 | 当阻塞队列满时,再往队列里add插入元素会抛出 java.lang.IllegalStateException: Queue full 当阻塞队列空时,在往队列里remove移除元素会抛出 java.util.NoSuchElementException |
特殊值 | 插入方法,成功true 失败false 移除方法,成功返回出队列的元素,队列里没有就返回null |
一直阻塞 | 当队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产线程直到put数据or响应中断退出 当队列空时,消费者试图从队列里take元素,队列会一直阻塞消费者线程直到队列可用 |
超时退出 | 当阻塞队列满时,队列会阻塞生产者一定时间,超过限时后生产者线程会退出 |
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(3);
// 抛出异常
blockingQueue.add("a");// 添加
blockingQueue.remove();// 删除
blockingQueue.remove("a");// 删除
blockingQueue.element();// 检查头部元素
// 阻塞
blockingQueue.put("b");// 添加
blockingQueue.take();// 删除
// 特殊值
blockingQueue.offer("c");// 添加
blockingQueue.poll();// 删除
blockingQueue.offer("d", 3, TimeUnit.SECONDS);
blockingQueue.poll(3, TimeUnit.SECONDS);
BlockingQueue<String> blockingQueue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "\tput 1");
blockingQueue.put("a");
System.out.println(Thread.currentThread().getName() + "\tput 2");
blockingQueue.put("b");
System.out.println(Thread.currentThread().getName() + "\tput 3");
blockingQueue.put("c");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "AAA").start();
new Thread(() -> {
try {
try { TimeUnit.MILLISECONDS.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + "\t" + blockingQueue.take());
try { TimeUnit.MILLISECONDS.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + "\t" + blockingQueue.take());
try { TimeUnit.MILLISECONDS.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + "\t" + blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "BBB").start();
阻塞队列的种类分析:
- ArrayBlockingQueue:由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue:由链表结构组成的有界阻塞队列。(默认大小为:Integer.MAX_VALUE)
- priorityBlockingQueue:支持优先级排序的无界阻塞队列。
- DelayQueue:使用优先级队列实现的延迟无界阻塞队列。
- SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。
- LinkedTransferQueue:由链表结构组成的无界阻塞队列。
- LinkedBlockingDeque:由链表结构组成的双向阻塞队列。
初始值为0的一个变量,两个线程对其交替操作,一个加1一个减1,来5轮
1、高并发 线程操作资源类
2、判断干活、唤醒通知
3、严防多线程并发状态下虚假唤醒
题目:一个初始值为零的变量,两个线程对其交替操作,一个加1一个减1
* 1、 线程 操作(方法) 资源类
* 2、 判断 干活 通知
* 3、 防止虚假唤醒机制
PS:多线程为什么要用while判断? 2个线程的情况用 if 运行正确,扩展到4个线程以上用 if 判断会出现虚假唤醒。
class ShareData{
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void incremenet()throws Exception{
lock.lock();
try {
// 1 判断
while(number != 0){
// 等待,不能生产
condition.await();
}
// 2、干活
number++;
System.out.println(Thread.currentThread().getName() + "\t" + number);
// 3、 通知唤醒
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decremenet()throws Exception{
lock.lock();
try {
// 1 判断
while(number == 0){
// 等待,不能生产
condition.await();
}
// 2、干活
number--;
System.out.println(Thread.currentThread().getName() + "\t" + number);
// 3、 通知唤醒
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
/**
* 题目:一个初始值为零的变量,两个线程对其交替操作,一个加1一个减1
* 1、 线程 操作(方法) 资源类
* 2、 判断 干活 通知
* 3、 防止虚假唤醒机制
*/
public class BlockingQueueDemo {
public static void main(String[] args) {
ShareData shareData = new ShareData();
new Thread(() -> {
for (int i = 1; i <= 5; i++) {
try {
shareData.incremenet();
} catch (Exception e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(() -> {
for (int i = 1; i <= 5; i++) {
try {
shareData.decremenet();
} catch (Exception e) {
e.printStackTrace();
}
}
},"BB").start();
}
}
题目:线程通信之顺序调用 ReentrantLock,实现A->B->C三个线程顺序执行
/**
* 多线程之间顺序调用,实现A->B->C三个线程启动,要求如下;
* AA打印5次,BB打印10次,CC打印15次
* 5循环10次
*/
class ShareResource{
private int number = 1; // A:1 B:2 C:3
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
public void print5(){
lock.lock();
try {
// 1、判断
while (number != 1){
c1.await();
}
// 2、干活
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
// 3、通知
number = 2; // 修改标志位
c2.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void print10(){
lock.lock();
try {
// 1、判断
while (number != 2){
c2.await();
}
// 2、干活
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
// 3、通知
number = 3; // 修改标志位
c3.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void print15(){
lock.lock();
try {
// 1、判断
while (number != 3){
c3.await();
}
// 2、干活
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
// 3、通知
number = 1; // 修改标志位
c1.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class SyncAndReentrantLockDemo {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareResource.print5();
}
},"AA").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareResource.print10();
}
},"BB").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareResource.print15();
}
},"CC").start();
}
}
题目:线程通信之生产者消费者阻塞 BlockingQueue队列版
class MyResource{
private volatile boolean FLAG = true; // 默认开启,进行生产+消费
private AtomicInteger atomicInteger = new AtomicInteger();
BlockingQueue<String> blockingQueue = null;
// 构造注入方法
public MyResource(BlockingQueue<String> blockingQueue) {
this.blockingQueue = blockingQueue;
System.out.println(blockingQueue.getClass().getName());
}
public void myProd()throws Exception{
String data = null;
boolean retValue = false;
while(FLAG){
data = atomicInteger.incrementAndGet() + "";
retValue = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);
if(retValue){
System.out.println(Thread.currentThread().getName() + "\t插入队列" + data +"成功");
}else{
System.out.println(Thread.currentThread().getName() + "\t插入队列" + data +"失败");
}
try { TimeUnit.MILLISECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
}
System.out.println(Thread.currentThread().getName() + "\t大老板叫停,表示flag=false,生产动作结束");
}
public void myConsumer()throws Exception{
while (FLAG){
String result = blockingQueue.poll(2, TimeUnit.SECONDS);
if(null == result || result.equalsIgnoreCase("")){
FLAG = false;
System.out.println(Thread.currentThread().getName() + "\t超过2秒钟没有取到蛋糕,消费退出");
System.out.println();
System.out.println();
return;
}
System.out.println(Thread.currentThread().getName() + "\t消费蛋糕" + result + "成功");
}
}
public void stop()throws Exception{
this.FLAG = false;
}
}
/**
* 线程通信之生产者消费者阻塞队列版
*/
public class ProdConsumer_BlockQueueDemo {
public static void main(String[] args) {
MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10));
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t生产线程启动");
try {
myResource.myProd();
} catch (Exception e) {
e.printStackTrace();
}
}, "Prod").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t消费线程启动");
try {
myResource.myConsumer();
} catch (Exception e) {
e.printStackTrace();
}
}, "Consumer").start();
try { TimeUnit.MILLISECONDS.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println();
System.out.println();
System.out.println("5秒钟到,大老板mian线程叫停,活动结束");
try {
myResource.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}