java多线程并发系列--基础知识点(笔试、面试必备)(下)

简介: 多个线程间锁的并发控制,对象锁多个线程、每个线程持有该方法所属对象的锁以及类锁。synchronized, wait, notify 是任何对象都具有的同步工具

13 多个线程间锁的并发控制


多个线程间锁的并发控制,对象锁多个线程、每个线程持有该方法所属对象的锁以及类锁。synchronized, wait, notify 是任何对象都具有的同步工具


对象锁的同步和异步


  • 同步:synchronized,同步的概念就是共享,只需要针对共享的资源,才需要考虑同步。
  • 异步:asynchronized,异步的概念就是独立,相互之间不受到任何制约。


同步的目的就是为线程安全,其实对于线程安全来说,需要满足两个特性:原子性(同步)、可见性。


14 Volatile关键字


Volatile作用,实现变量在多个线程间可见,保证内存可见性和禁止指令重排


多线程的内存模型:main memory(主存)、working


memory(线程栈),在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去(volatile关键词的作用:每次针对该变量的操作都激发一次load and save)。


25.png


15 ThreadLocal


线程局部变量,以空间换时间的手段,为每个线程提供变量的独立副本,以无锁的情况下保障线程安全。主要解决的就是让每个线程执行完成之后再结束,这个时候就要用到join()方法。


适用场景:


  • 在并发不是很高的时候,加锁的性能会更好
  • 在高并发量场景下,使用ThreadLocal可以在一定程度上减少锁竞争。


16 多线程同步和互斥实现方法


1). 线程同步,是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。


 线程间的同步方法,大体可分为两类:用户模式和内核模式。顾名思义:


内核模式,就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态。内核模式下的方法有:


  • 事件
  • 信号量
  • 互斥量


用户模式,就是不需要切换到内核态,只在用户态完成操作。用户模式下的方法有:


  • 原子操作(例如一个单一的全局变量)
  • 临界区


2). 线程互斥,是指对于共享的进程系统资源,在各单个线程访问时的排它性。


当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。 线程互斥可以看成是一种特殊的线程同步。


17 线程之间通信


线程是操作系统中独立的个体,但这些个体之间如果不经过特殊的协作就不能成为一个整体,线程间的通信就成为整体的必用方式之一。


线程间通信的几种方式?


线程之间的通信方式:


  • 共享内存
  • 消息传递


共享内存:在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信。典型的共享内存通信方式,就是通过共享对象进行通信。


26.png


消息传递:在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信。在 Java 中典型的消息传递方式,就是 wait()notify() ,或者 BlockingQueue 。


27.png


18 什么是 Java Lock 接口?


java.util.concurrent.locks.Lock 接口,比 synchronized 提供更具拓展行的锁操作。它允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的条件对象。它的优势有:


  • 可以使锁更公平。
  • 可以使线程在等待锁的时候响应中断。
  • 可以让线程尝试获取锁,并在无法获取锁的时候立即返回或者等待一段时间。
  • 可以在不同的范围,以不同的顺序获取和释放锁。


28.png


19 Java AQS


AQS ,AbstractQueuedSynchronizer ,即队列同步器。它是构建锁或者其他同步组件的基础框架(如 ReentrantLock、ReentrantReadWriteLock、Semaphore 等),J.U.C 并发包的作者(Doug Lea)期望它能够成为实现大部分同步需求的基础。它是 J.U.C 并发包中的核心基础组件。


 优势:


AQS 解决了在实现同步器时涉及当的大量细节问题,例如获取同步状态、FIFO 同步队列。基于 AQS 来构建同步器可以带来很多好处。它不仅能够极大地减少实现工作,而且也不必处理在多个位置上发生的竞争问题。


在基于 AQS 构建的同步器中,只能在一个时刻发生阻塞,从而降低上下文切换的开销,提高了吞吐量。同时在设计 AQS 时充分考虑了可伸缩性,因此 J.U.C 中,所有基于 AQS 构建的同步器均可以获得这个优势。


20 同步类容器


何为同步容器?可以简单地理解为通过synchronized来实现同步的容器,如果有多个线程调用同步容器的方法,它们将会串行执行。


 特点:


  • 是线程安全的
  • 某些场景下可能需要加锁来保护复合操作


 常见同步类容器:


  • 如Vector、HashTable
  • 使用JDK的Collections.synchronized等工厂方法去创建实现的。
  • 底层是用传统的synchronized关键字对方法进行同步。
  • 无法满足高并发场景下的性能需求


21 并发类容器


jdk5.0以后提供了多种并发类容器来替代同步类容器从而改善性能。


同步类容器局限性:


  • 都是串行化的。
  • 虽实现了线程安全,却降低了并发性
  • 在多线程环境时,严重降低了应用程序的吞吐量。


常用的并发类容器:


  • ConcurrentHashMap
  • Copy-On-Write容器


 ConcurrentHashMap原理


  • 内部使用段(Segment)来表示这些不同的部分
  • 每个段相当于一个小的HashTable,它们有自己的锁。
  • 把一个整体分成了16个段(Segment)。也就是最高支持16个线程的并发修改操作。
  • 这也是在多线程场景时通过减小锁的粒度从而降低锁竞争的一种方案。


 Copy-On-Write容器


Copy-On-Write简称COW,是一种用于程序设计中的优化策略。


  • 读写分离,读和写分开
  • 最终一致性
  • 使用另外开辟空间的思路,来解决并发冲突


 JDK里的COW容器有两种:


  • CopyOnWriteArrayList:适用于读操作远远多于写操作的场景,例如,缓存.
  • CopyOnWriteArraySet:线程安全的无序的集合,可以将它理解成线程安全的HashSet,适用于Set 大小通常保持很小,只读操作远多于可变操作,需要在遍历期间防止线程间的冲突


22 并发Queue


 并发Queue:


  • ConcurrentLinkedQueue,高性能队列,当许多线程共享访问一个公共集合时,ConcurrentLinkedQueue 是一个恰当的选择。
  • BlockingQueue,阻塞队列,是一个支持两个附加操作的队列,常用于生产者和消费者的场景。


 ConcurrentLinkedQueue


  • 适用于高并发场景
  • 使用无锁的方式,实现了高并发状态下的高性能
  • 其性能好于BlockingQueue
  • 遵循先进先出的原则


常用方法:


  • Add()和offer()都是加入元素的方法
  • Poll()和peek()都是取头元素节点,区别在于前者会删除元素,后者不会。


 BlockingQueue接口实现:


  • ArrayBlockingQueue:基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长的数组,以便缓存队列中的数据对象,其内部没实现读写分离,也就意味着生产和消费者不能完全并行,适用很多场景。


  • LinkedBlockingQueue:基于链表的阻塞队列,同ArrayBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),LinkedBlockingQueue之所以能够高效地处理并发数据,是因为其内部实现采用分离锁(读写分离两个锁),从而实现生产者和消费者操作完全并行运行。


  • PriorityBlockingQueue:基于优先级别的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定,也就是说传入队列的对象必须实现Comparable接口),在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁


  • DelayQueue:带有延迟时间的Queue,其中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue中的元素必须先实现Delayed接口,DelayQueue是一个没有大小限制的队列,应用场景很多,比如对缓存超时的数据进行移除、任务超时处理、空闲连接的关闭等等。


  • SynchronousQueue:一种没有缓冲的队列,生产者产生的数据直接会被消费者获取并且立刻消费


23 多线程的设计模式


  • 基于并行设计模式演变而来
  • 属于设计优化的一部分
  • 是对一些常用的多线程结构的总结和抽象
  • 常见的多线程设计模式有哪些?


24 Concurrent.util常用类


CountDownLatch: 用于监听某些初始化操作,等初始化执行完毕后,通知主线程继续工作。


CycilcBarrier: 所有线程都准备好后,才一起出发,只要有一个人没有准备好,大家都等待。


Concurrent.util常用类定义:实现异步回调,jdk针对该场景提供了一个实现的封装,简化了调用 适合场景:处理耗时的业务逻辑时,可有效的减少系统的响应时间,提高系统的吞吐量。


Concurrent.util常用类Semaphore:信号量,适合高并发访问, 用于进行访问流量的控制


ReentrantLock(重入锁)重入锁,在需要进行同步的代码部分加上锁定,但不要忘记最后一定要释放锁定,不然会造成锁永远无法释放,其他线程永远也进不来的结果。


 锁与等待/通知


  • 多线程间进行协作工作则需要Object的wait()和notify()、notifyAll()方法进行配合工作
  • 在使用锁的时候,可以使用一个新的等待/通知的类,它就是Condition
  • 这个Condition是针对具体某一把锁的


 多Condition


  • 可通过一个Lock对象产生多个Condition进行多线程间的交互
  • 使得部分需要唤醒的线程唤醒,其他线程则继续等待通知。


 ReentrantReadWriteLock(读写锁)


  • 其核心就是实现读写分离的锁。尤其适应在在高并发访问下读多写少的情况下,性能要远高于重入锁。
  • 将读写锁分离为读锁和写锁
  • 在读锁下,多个线程可以并发进行访问
  • 在写锁下,只能一个一个的顺序访问
  • 锁优化


25 线程池


29.png


 使用 Executor 框架的原因:


  • 每次执行任务创建线程 new Thread() 比较消耗性能,创建一个线程是比较耗时、耗资源的。
  • 调用 new Thread() 创建的线程缺乏管理,被称为野线程,而且可以无限制的创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪,还有线程之间的频繁交替也会消耗很多系统资源。
  • 接使用 new Thread() 启动的线程不利于扩展,比如定时执行、定期执行、定时定期执行、线程中断等都不便实现


 线程池的创建方式:


普通任务线程池


  • newFixedThreadPool(int nThreads) 方法,创建一个固定长度的线程池。


  • 每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化。
  • 当线程发生未预期的错误而结束时,线程池会补充一个新的线程。


  • newCachedThreadPool() 方法,创建一个可缓存的线程池。


  • 如果线程池的规模超过了处理需求,将自动回收空闲线程。
  • 当需求增加时,则可以自动添加新线程。线程池的规模不存在任何限制。


  • newSingleThreadExecutor() 方法,创建一个单线程的线程池。


  • 它创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代它。
  • 它的特点是,能确保依照任务在队列中的顺序来串行执行。


定时任务线程池


  • newScheduledThreadPool(int corePoolSize) 方法,创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似 Timer 。
  • newSingleThreadExecutor() 方法,创建了一个固定长度为 1 的线程池,而且以延迟或定时的方式来执行任务,类似 Timer 。


 线程池的关闭方式


ThreadPoolExecutor 提供了两个方法,用于线程池的关闭,分别是:


  • shutdown() 方法,不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务。
  • shutdownNow() 方法,立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务


26 总结


  由于java多线程并发涉及到的知识点太多了,这边不可能一一列全,不过我会在后续的更新中一一去补充完善,而且会涉及原理以及源码层次的解析。谢谢观看,有错误欢迎指出更改!!


各位看官还可以吗?喜欢的话,动动手指点个💗,点个关注呗!!谢谢支持!

目录
相关文章
|
7天前
|
SQL 分布式计算 监控
Sqoop数据迁移工具使用与优化技巧:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入解析Sqoop的使用、优化及面试策略。内容涵盖Sqoop基础,包括安装配置、命令行操作、与Hadoop生态集成和连接器配置。讨论数据迁移优化技巧,如数据切分、压缩编码、转换过滤及性能监控。此外,还涉及面试中对Sqoop与其他ETL工具的对比、实际项目挑战及未来发展趋势的讨论。通过代码示例展示了从MySQL到HDFS的数据迁移。本文旨在帮助读者在面试中展现Sqoop技术实力。
21 2
|
7天前
|
监控 负载均衡 Cloud Native
ZooKeeper分布式协调服务详解:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入剖析ZooKeeper分布式协调服务原理,涵盖核心概念如Server、Client、ZNode、ACL、Watcher,以及ZAB协议在一致性、会话管理、Leader选举中的作用。讨论ZooKeeper数据模型、操作、会话管理、集群部署与管理、性能调优和监控。同时,文章探讨了ZooKeeper在分布式锁、队列、服务注册与发现等场景的应用,并在面试方面分析了与其它服务的区别、实战挑战及解决方案。附带Java客户端实现分布式锁的代码示例,助力提升面试表现。
27 2
|
7天前
|
数据采集 消息中间件 监控
Flume数据采集系统设计与配置实战:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入探讨Apache Flume的数据采集系统设计,涵盖Flume Agent、Source、Channel、Sink的核心概念及其配置实战。通过实例展示了文件日志收集、网络数据接收、命令行实时数据捕获等场景。此外,还讨论了Flume与同类工具的对比、实际项目挑战及解决方案,以及未来发展趋势。提供配置示例帮助理解Flume在数据集成、日志收集中的应用,为面试准备提供扎实的理论与实践支持。
21 1
|
9天前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
9天前
|
算法 Java 开发者
Java中的多线程编程:概念、实现与性能优化
【4月更文挑战第9天】在Java编程中,多线程是一种强大的工具,它允许开发者创建并发执行的程序,提高系统的响应性和吞吐量。本文将深入探讨Java多线程的核心概念,包括线程的生命周期、线程同步机制以及线程池的使用。接着,我们将展示如何通过继承Thread类和实现Runnable接口来创建线程,并讨论各自的优缺点。此外,文章还将介绍高级主题,如死锁的预防、避免和检测,以及如何使用并发集合和原子变量来提高多线程程序的性能和安全性。最后,我们将提供一些实用的性能优化技巧,帮助开发者编写出更高效、更稳定的多线程应用程序。
|
7天前
|
安全 算法 Java
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第11天】 在Java中,高效的并发编程是提升应用性能和响应能力的关键。本文将探讨Java并发的核心概念,包括线程安全、锁机制、线程池以及并发集合等,同时提供实用的编程技巧和最佳实践,帮助开发者在保证线程安全的前提下,优化程序性能。我们将通过分析常见的并发问题,如竞态条件、死锁,以及如何利用现代Java并发工具来避免这些问题,从而构建更加健壮和高效的多线程应用程序。
|
2天前
|
存储 安全 Java
Java中的容器,线程安全和线程不安全
Java中的容器,线程安全和线程不安全
9 1
|
2天前
|
存储 缓存 安全
Java并发基础之互斥同步、非阻塞同步、指令重排与volatile
在Java中,多线程编程常常涉及到共享数据的访问,这时候就需要考虑线程安全问题。Java提供了多种机制来实现线程安全,其中包括互斥同步(Mutex Synchronization)、非阻塞同步(Non-blocking Synchronization)、以及volatile关键字等。 互斥同步(Mutex Synchronization) 互斥同步是一种基本的同步手段,它要求在任何时刻,只有一个线程可以执行某个方法或某个代码块,其他线程必须等待。Java中的synchronized关键字就是实现互斥同步的常用手段。当一个线程进入一个synchronized方法或代码块时,它需要先获得锁,如果
21 0
|
3天前
|
设计模式 运维 安全
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第15天】在Java开发中,多线程编程是提升应用程序性能和响应能力的关键手段。然而,它伴随着诸多挑战,尤其是在保证线程安全的同时如何避免性能瓶颈。本文将探讨Java并发编程的核心概念,包括同步机制、锁优化、线程池使用以及并发集合等,旨在为开发者提供实用的线程安全策略和性能优化技巧。通过实例分析和最佳实践的分享,我们的目标是帮助读者构建既高效又可靠的多线程应用。
|
5天前
|
Java 程序员 编译器
Java中的线程同步与锁优化策略
【4月更文挑战第14天】在多线程编程中,线程同步是确保数据一致性和程序正确性的关键。Java提供了多种机制来实现线程同步,其中最常用的是synchronized关键字和Lock接口。本文将深入探讨Java中的线程同步问题,并分析如何通过锁优化策略提高程序性能。我们将首先介绍线程同步的基本概念,然后详细讨论synchronized和Lock的使用及优缺点,最后探讨一些锁优化技巧,如锁粗化、锁消除和读写锁等。