【JavaEE】多线程代码实例:单例模式与阻塞队列BlockingQueue(二)

简介: 【JavaEE】多线程代码实例:单例模式与阻塞队列BlockingQueue

自实现阻塞队列:

实现阻塞队列的关键在于实现其阻塞的功能。其他的和普通的队列差不多。这里主要实现put和take方法:

1. 
2. class MyBlockingQueue{
3. 
4. //利用数组实现
5. private int[] arr=new int[1000];//设定数组长度为1000
6. 
7. private int size=0;//记录数组的内容长度
8. //利用end和begin两个指针使得数组变为循环数组(逻辑上的循环)
9. private int end=0;
10. private int begin=0;
11. 
12. //实现put方法
13. //阻塞考虑使用wait和notify进行唤醒(sleep不太靠谱)
14. public void put(int value) throws InterruptedException {
15. //判断是否满了(这里要用循环判断,因为在多线程当中,线程被唤醒的时候不一定不满)
16. //加锁保证原子性
17. synchronized (this){
18. while(size>= arr.length){
19. this.wait();
20.         }
21. //不满之后放入元素
22.             arr[end]=value;
23. //调整长度
24.             end++;
25.             size++;
26. //如果放满了则将end变为0
27. if(end>= arr.length){
28.                 end=0;
29.             }
30. //进行唤醒
31. this.notify();
32.         }
33.     }
34. //实现take方法
35. public int take() throws InterruptedException {
36. synchronized (this){
37. //判断是否为空
38. while (size==0){
39. this.wait();
40.             }
41. //不空之后开始取出元素
42. int ret=arr[begin];
43.             begin++;
44. if(begin>= arr.length){
45.                 begin=0;
46.             }
47.             size--;
48. this.notify();
49. return ret;
50.         }
51. 
52.     }
53. //长度
54. public synchronized int Size(){
55. return size;
56.     }
57. 
58. }
59. 
60. 
61. public class BlockingQueueDemo3 {
62. 
63. public static void main(String[] args) throws InterruptedException {
64. 
65.         MyBlockingQueue queue=new MyBlockingQueue();
66.         queue.put(100);
67.         queue.put(200);
68.         queue.put(300);
69.         System.out.println(queue.take());
70.         System.out.println(queue.take());
71.         System.out.println(queue.take());
72. 
73.     }
74. }

显然,当其中没有元素的时候就会阻塞等待。

生产者消费者模型:

生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。

生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取。

首先,阻塞队列相当于一个缓冲区,它平衡了生产者和消费者之间的处理能力。

其次,阻塞队列使得生产者和消费者之间的进行了解耦合,也就是消费者不再是直接依赖生产者。

对于阻塞队列,生产者是添加元素的一方,消费者是取元素的一方,产品是阻塞队列的元素。生产者和消费者通过阻塞队列相互联系。

画个图演示一下:

我们举向游戏里面氪金的例子吧!!!

这样就会出现一个问题:服务器A和服务器B的耦合太高,一旦其中一个服务器出现了问题,就会导致另一个服务器也无法完成需求。就会出现一个服务器挂了,把另一个服务器也带走的情况。

并且,如果我们需要在此基础上加一个新的服务器参与其他相关的功能,比如日志,也是会有问题的!

那么如何解决这种情况呢?这就用到了当前的生产者消费者模型。生产者生成的资源,我们可以将其放到一个阻塞队列当中去,当有消费者需要消费的时候,就直接从该阻塞队列当中去取,如果队列中没有资源,就阻塞等待,等待生产者进行生产,当阻塞队列满的时候,生产者也要进行阻塞等待。这里的服务器A就是生产者,服务器BC就是消费者。所以我们可以利用该模型进行这样的设计:

生产者消费者模型的实现:

利用系统的BlockingQueue实现生产者消费者模型:

首先我们利用系统提供的BlockingQueue实现生产者消费者模型:

1. import java.util.concurrent.BlockingQueue;
2. import java.util.concurrent.LinkedBlockingQueue;
3. 
4. public class BlockingQueueDemo2 {
5. public static void main(String[] args) {
6. //创建阻塞队列
7.         BlockingQueue<Integer>queue=new LinkedBlockingQueue<>();
8. //使用两个线程:一个线程充当生产者,一个线程充当消费者
9. //生产者
10.         Thread t1=new Thread(()->{
11. int count=0;
12. while(true){
13. try {
14.                     queue.put(count);
15.                     System.out.println("生产者生产:"+count);
16.                     count++;
17.                     Thread.sleep(500);
18.                 } catch (InterruptedException e) {
19. throw new RuntimeException(e);
20.                 }
21.             }
22.         });
23.         Thread t2=new Thread(()->{
24. 
25. while(true){
26. try {
27. int ret=queue.take();
28.                     System.out.println("消费者消费:"+ret);
29.                     Thread.sleep(500);
30.                 } catch (InterruptedException e) {
31. throw new RuntimeException(e);
32.                 }
33.             }
34.         });
35.         t1.start();
36.         t2.start();
37.     }
38. }

我们可以明确的看到,消费元素和生产元素是成对出现的。这就不会出现生产者没有生产出来的东西被消费的情况。

利用自实现的BlockingQueue实现生产者消费者模型:

1. 
2. class MyBlockingQueue1{
3. 
4. //利用数组实现
5. private int[] arr=new int[1000];//设定数组长度为1000
6. 
7. private int size=0;//记录数组的内容长度
8. //利用end和begin两个指针使得数组变为循环数组(逻辑上的循环)
9. private int end=0;
10. private int begin=0;
11. 
12. //实现put方法
13. //阻塞考虑使用wait和notify进行唤醒(sleep不太靠谱)
14. public void put(int value) throws InterruptedException {
15. //判断是否满了(这里要用循环判断,因为在多线程当中,线程被唤醒的时候不一定不满)
16. //加锁保证原子性
17. synchronized (this){
18. while(size>= arr.length){
19. this.wait();
20.             }
21. //不满之后放入元素
22.             arr[end]=value;
23. //调整长度
24.             end++;
25.             size++;
26. //如果放满了则将end变为0
27. if(end>= arr.length){
28.                 end=0;
29.             }
30. //进行唤醒
31. this.notify();
32.         }
33.     }
34. //实现take方法
35. public int take() throws InterruptedException {
36. synchronized (this){
37. //判断是否为空
38. while (size==0){
39. this.wait();
40.             }
41. //不空之后开始取出元素
42. int ret=arr[begin];
43.             begin++;
44. if(begin>= arr.length){
45.                 begin=0;
46.             }
47.             size--;
48. this.notify();
49. return ret;
50.         }
51. 
52.     }
53. //长度
54. public synchronized int Size(){
55. return size;
56.     }
57. 
58. }
59. 
60. 
61. public class BlockingQueueDemo4 {
62. public static void main(String[] args) throws InterruptedException {
63.         MyBlockingQueue1 queue1=new MyBlockingQueue1();
64. //生产者
65. Thread producer =new Thread(()->{
66. int count=0;
67. while(true){
68. try {
69.                     queue1.put(count);
70.                     System.out.println("生产者生产元素:"+count);
71.                     count++;
72.                     Thread.sleep(500);
73.                 } catch (InterruptedException e) {
74. throw new RuntimeException(e);
75.                 }
76.             }
77.         });
78. 
79. //消费者
80. Thread customer =new Thread(()->{
81. while(true){
82. try {
83. int ret=queue1.take();
84.                     System.out.println("消费者消费元素:"+ret);
85.                     Thread.sleep(500);
86.                 } catch (InterruptedException e) {
87. throw new RuntimeException(e);
88.                 }
89.             }
90.         });
91.         producer.start();
92.         customer.start();
93.     }
94. }

相关文章
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
581 3
|
供应链 安全 NoSQL
PHP 互斥锁:如何确保代码的线程安全?
在多线程和高并发环境中,确保代码段互斥执行至关重要。本文介绍了 PHP 互斥锁库 `wise-locksmith`,它提供多种锁机制(如文件锁、分布式锁等),有效解决线程安全问题,特别适用于电商平台库存管理等场景。通过 Composer 安装后,开发者可以利用该库确保在高并发下数据的一致性和安全性。
214 6
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
323 3
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
安全 Java 关系型数据库
单例模式下引发的线程安全问题
单例模式确保类在进程中仅有一个实例,适用于如数据库连接等场景。分为饿汉式与懒汉式:饿汉式在类加载时创建实例,简单但可能浪费资源;懒汉式延迟创建实例,需注意线程安全问题,常采用双重检查锁定(Double-Checked Locking)模式,并使用 `volatile` 关键字避免指令重排序导致的问题。
268 2
单例模式下引发的线程安全问题
|
设计模式 安全 Java
【多线程-从零开始-柒】单例模式,饿汉和懒汉模式
【多线程-从零开始-柒】单例模式,饿汉和懒汉模式
180 1
|
消息中间件 NoSQL 关系型数据库
【多线程-从零开始-捌】阻塞队列,消费者生产者模型
【多线程-从零开始-捌】阻塞队列,消费者生产者模型
189 0
|
3月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
198 6
|
6月前
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
354 83
|
3月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
396 0

热门文章

最新文章