深圳求做网站,阿里巴巴做国际网站多少钱,外网加速器试用七天,网络服务公司经营范围#x1f525;个人主页#xff1a; 中草药
#x1f525;专栏#xff1a;【Java】登神长阶 史诗般的Java成神之路 #x1f56f;️一.设计模式 在Java中#xff0c;设计模式#xff08;Design Patterns#xff09;是指在软件工程和面向对象编程中#xff0c;针对特定… 个人主页 中草药
专栏【Java】登神长阶 史诗般的Java成神之路 ️一.设计模式 在Java中设计模式Design Patterns是指在软件工程和面向对象编程中针对特定问题和常见情境的一种经过验证的解决方案模板。设计模式不是具体的代码而是一种描述问题和解决方案的通用形式它提供了一种在软件设计中重复使用的方法一种固定套路帮助开发者以更优雅、更灵活的方式解决常见的设计问题从而提高代码的可读性、可维护性和可扩展性。
设计模式通常基于以下几个方面
目的解决某一类问题或实现特定的功能。参与者涉及的类和对象。协作这些类和对象之间的交互方式。效果使用模式后带来的好处和可能的权衡。
设计模式通常被分类为三种类型 创建型模式Creational Patterns关注对象的创建机制试图创建对象的过程能够满足一定的约束条件例如单例模式Singleton、工厂模式Factory、抽象工厂模式Abstract Factory、建造者模式Builder和原型模式Prototype。 结构型模式Structural Patterns关注类和对象的组合以达到更灵活和可复用的结构例如适配器模式Adapter、装饰模式Decorator、代理模式Proxy、桥接模式Bridge、组合模式Composite和外观模式Facade。 行为型模式Behavioral Patterns关注类和对象之间的职责分配和交互涉及算法的封装和对象之间的通信例如策略模式Strategy、模板方法模式Template Method、观察者模式Observer、命令模式Command、迭代器模式Iterator、访问者模式Visitor、中介者模式Mediator和状态模式State。
不同的设计模式的使用可以带来以下优势
代码复用通过模式可以复用解决问题的方案避免重复造轮子。可维护性模式往往伴随着良好的代码组织使得代码更易于理解和维护。可扩展性模式鼓励使用接口和抽象类使得系统更易于扩展和修改。灵活性模式强调松耦合使得系统更加灵活能够适应变化。 设计模式并非万能药过度使用或不恰当使用设计模式可能导致代码过度复杂增加理解难度。因此在应用设计模式时应根据实际情况和项目需求灵活选择最合适的模式。 在这里我们着重来学习单例模式和阻塞队列
️二.单例模式
单例模式Singleton Pattern是一种常用的软件设计模式用于确保一个类只有一个实例并提供一个全局访问点来访问这个实例。这种模式在需要频繁创建和销毁对象开销较大或者某个资源只能由一个实例独占使用的场景中非常有用。单例模式通常用于创建日志对象、对话框、数据库连接、配置管理器等。
1.单例模式的实现
单例模式的主要目标是控制一个类的实例化过程确保任何时候都只有一个实例存在并且提供一个全局访问点。以下是几种常见的实现方式 懒汉式Lazy Initialization 这是最直观的实现方式只有在首次请求实例时才创建实例。 public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance null) {instance new Singleton();}return instance;}
} 但是上面的代码在多线程环境下可能不是线程安全的因为多个线程可能同时进入 if 语句判断导致创建多个实例。改进的版本可以使用双重检查锁定Double-Checked Locking class SingletonLan{private static volatile SingletonLan instancenull;//1.volatile内存可见性问题private SingletonLan() {}public static SingletonLan getInstance() {if (instancenull){//2.判断是否要加锁synchronized (locker) {//3.加锁把if判定和new赋值操作打包成原子操作if (instance null) {//4.是否要创建对象instance new SingletonLan();}}}return instance;}
} 饿汉式Eager Initialization 这种方式在类加载时就创建实例因此不存在多线程安全问题。 public class Singleton {private static final Singleton instance new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
} 除此之外还有静态内部类实现这种方式结合了懒汉式的延迟加载和饿汉式的线程安全利用了Java类加载机制保证初始化实例时只有一个线程。 以及《Effective Java》作者Joshua Bloch推荐的枚举方式简洁并且线程安全。
2.优缺点
优点
确保一个类只有一个实例节省内存和资源。全局唯一访问点便于控制和扩展。
缺点
单例模式可能会隐藏类之间的依赖关系因为单例类的实例是静态的不容易在代码中显式表达。单例模式可能违反单一职责原则一个类负责实例的创建和管理同时也负责业务逻辑。在多线程环境下实现线程安全的单例模式需要额外的注意和开销。
单例模式是一种强大的设计模式可以有效地控制资源的使用特别是在多线程和网络环境中。然而它也有可能引入一些不易察觉的问题因此在使用时应当谨慎并充分考虑具体的应用场景和潜在的影响。 三.阻塞队列
1.生产者消费者模型 生产者消费者模型是计算机科学中用于解决多线程或并发编程中资源分配和数据共享问题的经典模型。这个模型主要用于描述一组生产数据的进程或线程和一组消费这些数据的进程或线程之间的交互。它是实现资源管理、数据缓冲和流程控制的有效方式特别是在多线程和分布式系统中。 基本概念
在生产者消费者模型中有两组主要的角色
生产者Producer负责生成数据或资源并将它们放入一个共享容器如队列、缓冲区中。消费者Consumer负责从共享容器中取出数据或资源并对其进行处理。
这两个角色通过一个共享的缓冲区或队列进行通信和数据交换。生产者将数据放入队列而消费者从队列中取出数据进行处理。
工作流程
生产者消费者模型的工作流程如下 生产者向队列中添加数据当生产者生成数据时它会尝试将数据放入队列中。如果队列已满生产者可能需要等待直到有空间可用。 消费者从队列中移除数据当消费者准备好处理数据时它会从队列中取出数据。如果队列为空消费者可能需要等待直到队列中有数据可用。 同步和通信为了确保数据的正确处理生产者和消费者必须通过某种机制如锁、信号量或条件变量进行同步以避免数据竞争和不一致性。
解决的问题
生产者消费者模型解决了以下问题
资源竞争通过使用同步机制可以防止生产者和消费者同时访问共享资源从而避免数据的混乱或丢失。缓冲区管理队列或缓冲区充当了生产者和消费者之间的中间层可以吸收生产速率和消费速率之间的差异避免生产者和消费者的直接耦合。并发控制模型允许并发执行提高系统的吞吐量和响应速度。
实现细节
在实际编程中实现生产者消费者模型通常涉及到以下技术
线程和进程生产者和消费者可以是不同的线程或进程。同步原语使用互斥锁mutex、信号量semaphore、条件变量condition variable等来确保数据的正确性和一致性。队列和缓冲区实现数据的存储和传递可以是基于数组、链表或其他数据结构的队列。
Java中的实现
在Java中可以使用java.util.concurrent包中的BlockingQueue接口来实现生产者消费者模型该接口提供了线程安全的队列实现如ArrayBlockingQueue、LinkedBlockingQueue等它们内置了阻塞机制可以简化同步和通信的实现。
应用场景
生产者消费者模型广泛应用于各种场景包括但不限于
消息队列如RabbitMQ、Kafka等用于在微服务架构中解耦服务间的通信。任务调度和执行框架如Apache Airflow、Apache Beam等用于大规模数据处理和分析任务的调度。多媒体处理在视频编码、音频流处理等场景中用于处理连续的数据流。游戏开发在渲染引擎中生产者可能负责生成游戏世界的帧而消费者则负责渲染这些帧。
总之生产者消费者模型是解决多线程和并发问题的一个强大工具它通过分离数据的生产和消费过程提高了系统的可扩展性和可靠性。
2.阻塞队列
在Java中阻塞队列Blocking Queue是一种特殊类型的队列它提供了额外的阻塞行为。通常队列是一种先进先出FIFO的数据结构其中元素的插入操作在队列的尾部进行而移除操作在队列的头部进行。阻塞队列在标准队列的基础上增加了线程安全性和阻塞能力使其成为多线程环境中处理任务的理想选择。
特点 // 创建一个容量为5的阻塞队列
BlockingQueueInteger queue new ArrayBlockingQueue(5);
阻塞队列的主要特点如下 线程安全性阻塞队列的所有方法都是线程安全的这意味着多个线程可以同时访问队列而不会引起数据不一致的问题。 阻塞行为当队列满时put方法会阻塞调用线程直到队列中有可用空间为止。同样当队列空时take方法会阻塞调用线程直到队列中有新元素被加入。这种阻塞行为有助于线程间的同步和通信。 容量限制阻塞队列通常具有固定的容量限制这有助于防止无限的内存消耗。
常见的阻塞队列类型
Java并发工具包java.util.concurrent包提供了几种不同类型的阻塞队列实现每种实现都有其独特的特性和适用场景 ArrayBlockingQueue基于数组的有界阻塞队列。它使用公平锁和非公平锁两种模式并且是线程安全的。 LinkedBlockingQueue基于链表的阻塞队列。它有两个构造器一个是有界的另一个是无界的。当使用无界构造器时队列的大小只受限于系统可用的内存。 PriorityBlockingQueue基于优先级堆的无界阻塞队列。元素按优先级排序当多个元素具有相同优先级时它们按照FIFO顺序排列。 SynchronousQueue一种特殊的阻塞队列它不存储元素而是在生产者线程和消费者线程之间直接传递元素。这使得它非常轻量级但不适合存储元素。 DelayQueue一种特殊类型的队列它只保存延期元素。元素只有在其延迟过期后才能从队列中取出。
使用场景
阻塞队列广泛应用于多线程编程中尤其在以下场景中 线程池线程池使用阻塞队列来管理待处理的任务当线程空闲时它们会从队列中获取任务来执行。 生产者-消费者模型阻塞队列是实现生产者-消费者模式的理想选择生产者向队列中添加元素而消费者从队列中取出元素。 任务调度阻塞队列可以用于任务的调度比如定时任务或基于事件的任务。 资源池管理例如数据库连接池或缓存池阻塞队列可以用来管理有限的资源。
总结
阻塞队列是Java并发编程中一个非常重要的概念它提供了一种线程安全且高效的机制来管理多线程环境下的任务调度和资源分配。通过选择合适的阻塞队列类型可以有效地控制并发级别提高系统的响应能力和吞吐量。
3.模拟实现
我们可以模拟一个基于数组的阻塞队列
实现代码
class MyBlockingQueue{private int head 0;private int tail 0;private int size 0;private String[] datanull;public MyBlockingQueue(int capacity){datanew String[capacity];}public void put(String s) throws InterruptedException {synchronized(this){if (sizedata.length){this.wait();//如果wait在try catch里面// 此时应该用while 在wait唤醒之后判断是否继续执行 如t2}data[tail]s;tail;if (taildata.length){tail0;}size;this.notify();}}public String take() throws InterruptedException{String retnull;synchronized(this){while (size0){this.wait();}retdata[head];head;if (headdata.length){head0;}size--;this.notify();}return ret;}
}测试代码
public static void main(String[] args) {MyBlockingQueue queuenew MyBlockingQueue(1000);Thread t1new Thread(()-{int i1;while(true){try {queue.put(i);System.out.println(生产元素i);i;Thread.sleep(1000);//生产慢点} catch (InterruptedException e) {e.printStackTrace();}}});Thread t2new Thread(()-{while(true){try {int curInteger.parseInt(queue.take());System.out.println(取出元素 cur);//Thread.sleep(1000);//生产快点消费慢点} catch (InterruptedException e) {e.printStackTrace();}}});t1.start();t2.start();}
测试结果
1.当生产慢于消费者时 2.当生产快于消费者时 四.总结与反思 梦想家命长实干家寿短。——约奥赖利 在深入探索软件设计模式与多线程编程技术的过程中Java中的单例模式与阻塞队列成为了我关注的焦点。这两个概念虽然分别属于设计模式和并发控制领域但它们都在提高代码质量和系统性能方面扮演着关键角色。下面是对这两项技术的学习总结与个人反思。
单例模式Singleton Pattern
定义与目的 单例模式是一种常用的软件设计模式其核心目标是在整个系统中保证一个类只有一个实例并提供一个全局访问点。这有助于节省资源确保共享资源的一致性比如数据库连接、配置管理器等。
实现方式
懒汉式Lazy Initialization在首次使用时创建实例适用于延迟加载的情况。饿汉式Eager Initialization在类加载时就创建实例适合于系统启动时就需要初始化的情况。双重检查锁定Double Checked Locking结合懒汉式的延迟加载和同步控制确保线程安全的同时减少锁的竞争。
反射与序列化挑战 单例模式通过私有构造函数和静态工厂方法或枚举来实现但反射和序列化可能会破坏单例性质。解决办法是重写clone方法和readResolve方法来保持单例的唯一性。
个人反思 单例模式虽然简单但在复杂系统中应用时需谨慎。过度使用可能导致系统变得难以测试和维护。同时随着微服务架构的流行单例模式的全局性也需要重新审视因为它可能不再适用于分布式环境。
阻塞队列Blocking Queue
定义与目的 阻塞队列是一种特殊的队列它在队列满时阻止生产者线程继续添加元素在队列空时阻止消费者线程取出元素直到条件满足。这样可以有效控制线程间的同步避免资源竞争和死锁问题。
应用场景
生产者消费者模型用于协调不同线程之间的数据传递如任务调度、消息队列等。限流与缓冲在高并发场景下阻塞队列可以作为缓冲区防止后端系统过载。
Java中的实现
ArrayBlockingQueue基于数组的阻塞队列固定大小。LinkedBlockingQueue基于链表的阻塞队列可选择固定或无限大小。PriorityBlockingQueue基于优先级堆的阻塞队列适用于需要按优先级处理任务的场景。
个人反思 阻塞队列的使用提升了系统的健壮性和可扩展性尤其是在多线程环境下。然而正确配置队列的大小和理解队列的阻塞机制至关重要否则可能会导致性能瓶颈或资源浪费。此外阻塞队列的灵活性也意味着开发者需要对线程的交互和队列的行为有深入的理解。
总结
学习单例模式和阻塞队列不仅加深了我对Java语言特性的理解也让我认识到在设计高效、健壮的系统时选择合适的设计模式和并发控制策略的重要性。未来在开发项目时我会更加注重模式的适用性和潜在的副作用努力构建既灵活又稳定的软件架构。 以上就是本期的全部内容啦若有错误疏忽希望各位大佬及时指出 制作不易希望能对各位提供微小的帮助可否留下你免费的赞呢