在分布式开发中,有哪些并发设计模式可以用?
并发设计模式与常说的23种设计模式有何不同?
在分布式开发中,有几种常见的并发设计模式用于解决并发编程中的问题和挑战。以下是一些常见的并发设计模式:
并发设计模式与常说的23种设计模式(通常指GoF设计模式)的主要区别在于它们的关注点和应用场景。GoF设计模式主要关注对象之间的交互和职责分配,而并发设计模式更侧重于解决并发编程中的挑战,如数据一致性、线程安全、性能优化等。
希望这个答案对你有帮助!如果你还有其他问题或需要更详细的解释,请随时提问。
3 回答2.2k 阅读✓ 已解决
3 回答3.7k 阅读✓ 已解决
8 回答2.9k 阅读
4 回答2.3k 阅读✓ 已解决
3 回答2.2k 阅读✓ 已解决
3 回答1.5k 阅读✓ 已解决
1 回答1.9k 阅读✓ 已解决
与23种设计模式考虑的场景不同,在分布式并发应用中,还有一些常用的并发模式,V哥今天给大家整理了18种并发下的设计模式,从概念,原理分析,示例代码和应用场景方面来全面介绍,这会帮助你在并发编程中即学即用。
多线程并发设计模式是在多线程程序设计中经常使用的一些解决方案,它们帮助解决特定的问题,提高程序的性能和可维护性。以下是一些常见的多线程并发设计模式:
1. 单例模式(Singleton):
2. 不可变对象模式(Immutable Object):
3. 线程局部存储模式(Thread Local Storage):
4. 生产者-消费者模式(Producer-Consumer):
5. 读者-写者模式(Read-Write Lock):
6. 工作队列模式(Worker Thread):
7. 线程池模式(Thread Pool):
8. future模式(Future):
9. 屏障模式(Barrier):
10. 计数信号量模式(Counting Semaphore):
11. 守护线程模式(Daemon Thread):
12. 枚举线程模式(Thread Enumeration):
13. 线程中断模式(Thread Interruption):
14. 两阶段终止模式(Two-Phase Termination):
15. 工作窃取模式(Work Stealing):
16、保护性暂挂模式(Guarded Suspension)
17、放弃模式(Balking)
18. Thread-Per-Message 模式
这些设计模式是处理多线程并发问题时的重要工具,可以帮助开发者编写出高效、安全、可维护的并发程序。在实际应用中,可能需要根据具体情况选择合适的设计模式,或者将几种模式组合使用。
下面 V 哥一个一个详细来介绍。
1、单例模式(Singleton)
概念
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。这意味着一旦创建了一个类的实例,所有对该类的进一步调用都将返回这个已创建的实例,而不是创建一个新的实例,这个跟我们普通写的单例是同一个,只是加入并发场景的需求。
实现原理
并发代码示例
在多线程环境中,实现单例模式需要考虑线程安全问题。以下是使用“双重检查锁定”(Double-Checked Locking)的线程安全单例模式实现:
在上面的代码中,getInstance方法首先检查实例是否已创建,如果没有,则进入同步块。在同步块内,再次检查实例是否已创建,这样做是为了防止在多线程环境下多次创建实例。使用volatile关键字确保instance变量的可见性和有序性,避免指令重排问题。
应用场景
注意事项
单例模式比较简单,也是Java设计中非常基础且广泛使用的设计模式,它在许多框架和应用程序中都有应用。正确实现单例模式可以确保资源的有效管理和全局访问的一致性。
2、不可变对象模式(Immutable Object)
概念
不可变对象模式是一种创建型设计模式,它指的是一旦创建了一个对象,其状态就不能被修改。不可变对象在创建后其内部状态保持不变,任何试图修改状态的尝试都会创建一个新的对象。
实现原理
并发代码示例
不可变对象天然支持并发,因为它们的状态不可变,不会出现线程安全问题。以下是一个简单的不可变对象的示例:
在上面的代码中,ImmutablePerson类的属性name和age都是final的,确保了它们在对象创建后不会被修改。如果需要修改这些属性,setName和setAge方法会创建一个新的ImmutablePerson对象,这个一个V哥觉得最简单的并发模式,只要理解其中的含义就可以实现。
应用场景
注意事项
不可变对象模式在多线程环境中非常有用,因为它可以避免并发访问时的同步问题,同时也可以提高程序的安全性和可预测性。
3、线程局部存储模式(Thread Local Storage)
概念
线程局部存储模式是一种行为设计模式,它允许在多线程环境下为每个线程维护一个独立的变量副本。这意味着每个线程都有自己独立的变量实例,从而避免了线程安全问题。
实现原理
并发代码示例
以下是一个使用ThreadLocal的示例,它为每个线程提供了一个独立的变量副本:
在这个示例中,主线程和子线程分别设置和获取了ThreadLocal变量的值。由于ThreadLocal为每个线程提供了独立的副本,因此两个线程之间的值是相互独立的。
应用场景
注意事项
线程局部存储模式在多线程编程中非常有用,它提供了一种简单的方式来管理和维护线程级别的数据,同时避免了同步的开销。
4、生产者-消费者模式(Producer-Consumer)
概念
生产者-消费者模式是一种行为设计模式,它通过将数据的生成(生产者)和数据的处理(消费者)分离来解决这个问题。在这种模式中,生产者负责创建数据,放入一个共享的数据结构中,而消费者则从该数据结构中取出数据进行处理。这种模式可以有效地解决生产者和消费者之间的同步问题,并允许多个生产者和消费者同时操作。
实现原理
并发代码示例
以下是一个使用BlockingQueue实现的生产者-消费者模式的示例:
在这个示例中,Producer线程将数据放入BlockingQueue中,而Consumer线程从BlockingQueue中取出数据进行处理。BlockingQueue自动处理了同步和阻塞逻辑。
应用场景
注意事项
生产者-消费者模式在多线程编程中非常重要,它提供了一种高效的方式来处理数据的生成和处理,同时保持了系统组件之间的解耦,这个模式也是经典的多线程协同工作案例,记得 V 哥在基础阶段讲解多线程时就采用了这个案例。
5、读者-写者模式(Read-Write Lock)
概念
读者-写者模式是一种同步机制,允许多个读者同时访问数据,但在写者访问时,其他的读者或写者都会被阻塞。这种模式适用于读操作远多于写操作的场景,它通过减少锁的竞争来提高系统的并发性能。
实现原理
并发代码示例
以下是一个使用ReentrantReadWriteLock实现的读者-写者模式的示例:
在这个示例中,SharedResource类包含了一个ReadWriteLock,用于保护数据data。读者线程调用read方法来读取数据,而写者线程调用write方法来写入数据。读锁和写锁保证了数据的一致性和并发访问的控制。
应用场景
注意事项
V哥认为,读者-写者模式在多线程编程中非常有用,它提供了一种高效的方式来处理
读多写少
的场景,同时保持了数据的一致性和并发访问的控制。6、工作队列模式(Worker Thread)
概念
工作队列(工作线程)模式是一种行为设计模式,它将任务的提交与任务的执行分离。在这种模式中,任务被提交到一个工作队列中,而后台线程(工作者线程)从队列中取出任务并执行。这种模式可以有效地管理任务的执行,提高系统的并发性能。
实现原理
并发代码示例
以下是一个使用ExecutorService实现的工作队列模式的示例:
在这个示例中,ExecutorService创建了一个固定大小的线程池,用于执行提交的任务。客户端通过调用submit方法将Task对象提交到线程池中,工作者线程会从队列中取出任务并执行。
应用场景
注意事项
工作队列模式在多线程编程中同样非常有用,它提供了一种高效的方式来管理和执行任务,同时保持了系统组件之间的解耦。 V 哥提醒一下,你是不是隐隐约约想到了消息对列,是的,就是它。
7、线程池模式(Thread Pool)
概念
线程池模式是一种创建型设计模式,它通过复用一组线程来执行多个任务,从而减少了线程创建和销毁的开销。在这种模式中,线程池管理一个线程集合,线程池负责分配任务给这些线程,并在任务完成后将线程返回到池中,以便重新使用。
实现原理
并发代码示例
以下是一个使用ExecutorService实现的线程池模式的示例:
在这个示例中,ExecutorService创建了一个固定大小的线程池,用于执行提交的任务。客户端通过调用submit方法将Task对象提交到线程池中,线程池中的线程会从队列中取出任务并执行。
应用场景
注意事项
线程池模式在多线程编程中非常有用,它提供了一种高效的方式来管理和执行任务,同时减少了线程创建和销毁的开销,使用线程池非常常见了,V 哥觉得,你在面试时被问到线程池的实现原理时,这个实现原理可以用上。
8、Future模式(Future)
概念
Future模式是一种行为设计模式,它允许异步计算的结果在未来某个时间点获取。在这种模式中,一个任务被提交给一个执行器(如线程池),并返回一个Future对象,该对象可以用来检查任务是否已完成,以及获取任务的结果。
实现原理
并发代码示例
以下是一个使用ExecutorService和Future实现的Future模式的示例:
在这个示例中,LongRunningTask是一个Callable任务,它执行一个耗时操作并返回一个结果。Future对象future被用来检查任务是否已完成,并获取任务的结果。
应用场景
注意事项
Future模式在多线程编程中也是常用的不要不要的,它提供了一种高效的方式来执行异步计算,同时保持了系统组件之间的解耦,Future必须拿下。
8、屏障模式(Barrier)
概念
屏障模式是一种同步机制,它允许一组线程在到达一个屏障点时同时阻塞,直到所有线程都到达这个点,屏障才会打开,所有线程才会继续执行。这种模式通常用于多线程任务的分阶段执行,确保所有线程都完成一个阶段后,才能开始下一个阶段。
实现原理
并发代码示例
以下是一个使用CyclicBarrier实现的屏障模式的示例:
在这个示例中,CyclicBarrier被创建,并设置为当有5个线程到达屏障点时打开。每个线程在到达屏障点时都会被阻塞,直到所有线程都到达这个点。当所有线程都到达屏障点时,屏障会打开,所有线程继续执行。
应用场景
注意事项
下面这句话V哥写了很多遍了,但还是要说,下面还会再说,不重要的话V哥写它干毛用:屏障模式在多线程编程中非常有用,它提供了一种高效的方式来确保多线程任务在特定点同步执行,从而提高了系统的并发性能。
10、计数信号量模式(Counting Semaphore)
概念
计数信号量模式是一种同步机制,它允许控制对共享资源的访问。在这种模式中,信号量维护一个许可数,线程必须获得一个许可才能访问资源。当所有许可都被占用时,试图获取许可的线程会被阻塞,直到有许可被释放。
实现原理
并发代码示例
以下是一个使用Semaphore实现的计数信号量模式的示例:
在这个示例中,Semaphore被创建,并设置为有2个可用许可。每个线程在访问资源之前必须获取一个许可,当所有许可都被占用时,其他线程会被阻塞。当线程完成对资源的访问后,会释放一个许可,使得其他阻塞的线程可以获取许可并继续执行。
应用场景
注意事项
计数信号量模式在多线程编程中非常有用,它提供了一种高效的方式来控制对共享资源的并发访问,从而提高了系统的并发性能。
11、守护线程模式(Daemon Thread)
概念
守护线程模式是一种线程配置模式,它指定了线程的守护状态。当一个Java虚拟机中所有非守护线程结束时,Java虚拟机会退出。守护线程的目的是在主线程之外执行一些后台任务,这些任务不必须完成,因为它们不会阻止Java虚拟机退出。
实现原理
并发代码示例
以下是一个使用守护线程模式的示例:
在这个示例中,nonDaemonThread是一个非守护线程,它会无限循环执行任务。daemonThread是一个守护线程,它也会无限循环执行任务,但是当所有非守护线程结束时,Java虚拟机会退出,守护线程也会随之结束。
应用场景
注意事项
守护线程模式在多线程编程中非常有用,它提供了一种高效的方式来执行后台任务,同时不会阻止Java虚拟机退出。
12、枚举线程模式(Thread Enumeration)
概念
枚举线程模式是一种操作设计模式,它提供了一种遍历系统中所有线程的方法。在这种模式中,可以获取当前Java虚拟机中所有线程的枚举,并对其进行操作,如获取线程信息、修改线程状态等。
实现原理
并发代码示例
以下是一个使用Thread.getAllStackTraces()实现的枚举线程模式的示例:
在这个示例中,ThreadMXBean被用来获取所有线程的信息。getThreadInfo方法返回一个包含所有线程信息的数组,通过遍历这个数组,可以获取每个线程的ID、名称、状态和堆栈跟踪信息。
应用场景
注意事项
枚举线程模式在多线程编程中非常有用,它提供了一种高效的方式来获取和操作线程,从而提高了系统的可维护性和健壮性。
13、线程中断模式(Thread Interruption)
概念
线程中断模式是一种行为设计模式,它允许线程在执行过程中被中断,从而使得线程可以优雅地退出。在这种模式中,当一个线程接收到中断信号时,它可以检查是否需要响应中断,并采取相应的操作,如清理资源、保存状态或直接退出。
实现原理
并发代码示例
以下是一个使用线程中断模式的示例:
在这个示例中,InterruptibleThread类包含了一个无限循环,该循环会持续运行,直到接收到中断信号。当主线程等待一段时间后,它通过调用thread.interrupt()方法来发送中断信号。在循环内部,线程会检查是否接收到中断信号,如果接收到,它会打印一条消息并退出循环。
应用场景
注意事项
线程中断模式在多线程编程中非常有用,它提供了一种高效的方式来优雅地退出线程,从而提高了系统的可维护性和健壮性。
14、两阶段终止模式(Two-Phase Termination)
概念
两阶段终止模式是一种线程管理设计模式,它允许一个线程安全地停止另一个线程。这种模式分为两个阶段:准备阶段和终止阶段。在准备阶段,目标线程会做一些必要的清理工作,如释放资源、保存状态等。在终止阶段,目标线程会退出,从而结束执行。
实现原理
并发代码示例
以下是一个使用两阶段终止模式的示例:
在这个示例中,TwoPhaseTerminationExample类包含了一个无限循环,该循环会持续运行,直到接收到中断信号。当主线程等待一段时间后,它通过调用targetThread.interrupt()方法来发送中断信号。在循环内部,线程会检查是否接收到中断信号,如果接收到,它会打印一条消息并退出循环。
应用场景
注意事项
两阶段终止模式在多线程编程中非常有用,它提供了一种高效的方式来安全地停止线程,从而提高了系统的可维护性和健壮性。
15、作窃取模式(Work Stealing)
概念
工作窃取模式是一种线程调度算法,它允许线程从其他线程的队列中窃取任务来执行。这种模式通常用于实现线程池,其中线程会尝试从其他队列中窃取任务,以提高系统的并发性能。
实现原理
并发代码示例
以下是一个使用工作窃取模式的示例:
在这个示例中,WorkStealingExample类创建了一个固定大小的线程池和一个任务队列数组。每个任务队列都包含一个特定的任务队列。当线程池中的线程完成自己的任务队列时,它会尝试从其他队列中窃取任务。
应用场景
注意事项
工作窃取模式在多线程编程中非常有用,它提供了一种高效的方式来管理和执行任务,同时保持了系统组件之间的解耦。
16、保护性暂挂模式(Guarded Suspension)
概念
保护性暂挂模式是一种同步机制,用于在多线程环境下处理线程的暂挂和唤醒。在这种模式中,线程在等待某个条件满足时会被暂时挂起,当条件满足时,挂起的线程会被唤醒并继续执行。
实现原理
并发代码示例
以下是一个使用保护性暂挂模式的示例:
在这个示例中,GuardedSuspensionExample类包含了一个ReentrantLock和一个Condition。conditionMet变量用于表示条件是否满足。主线程和子线程分别等待和设置条件。当子线程设置条件时,主线程会被唤醒并执行后续代码。
应用场景
注意事项
保护性暂挂模式在多线程编程中非常有用,它提供了一种高效的方式来控制线程的暂挂和唤醒,从而提高了系统的并发性能。
17、放弃模式(Balking)
概念
放弃模式是一种同步机制,用于在多线程环境下避免重复执行某些操作。在这种模式中,当一个线程尝试执行某个操作时,它会检查其他线程是否已经执行了该操作,如果是,则该线程会放弃执行该操作。
实现原理
并发代码示例
以下是一个使用放弃模式的示例:
在这个示例中,BalkingExample类包含了一个AtomicBoolean变量,用于表示操作是否已经完成。两个线程尝试执行操作,如果操作已经完成,它们会放弃执行。
应用场景
注意事项
放弃模式在多线程编程中非常有用,它提供了一种高效的方式来避免重复执行某些操作,从而提高了系统的可维护性和健壮性。
18、Thread-Per-Message 模式
概念
Thread-Per-Message模式是一种多线程编程的设计模式,它为每个消息创建一个独立的线程来处理。在这种模式中,每个消息都会启动一个新的线程来执行任务,这样可以确保每个任务都是独立的,不会受到其他任务的影响。
实现原理
并发代码示例
以下是一个使用线程-每-消息模式的示例:
在这个示例中,ThreadPerMessageExample类创建了一个消息队列和一个线程池。每个消息都会启动一个新的线程来执行任务。当消息队列不为空时,线程池中的线程会不断地从消息队列中取出消息并执行。
应用场景
注意事项
Thread-Per-Message在多线程编程中非常有用,它提供了一种高效的方式来管理和执行任务,同时保持了系统组件之间的解耦。
最后
最后再调强一下,
这些模式在多线程并发编程中非常有用
。在分布式应用中,并发场景无处不在,理解和掌握这些并发模式的编码技巧,有助于我们在开发中解决很多问题,这要把这些与23种设计模式混淆了,虽然像单例模式是同一个,但这个是考虑并发场景下的应用。内容比较多,V哥建议可以收藏起来,即用好查。拜拜了您誒。