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


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

目录
相关文章
|
1月前
|
缓存 安全 Java
java面试-基础语法与面向对象
本文介绍了 Java 编程中的几个核心概念。首先,详细区分了方法重载与重写的定义、发生阶段及规则;其次,分析了 `==` 与 `equals` 的区别,强调了基本类型和引用类型的比较方式;接着,对比了 `String`、`StringBuilder` 和 `StringBuffer` 的特性,包括线程安全性和性能差异;最后,讲解了 Java 异常机制,包括自定义异常的实现以及常见非检查异常的类型。这些内容对理解 Java 面向对象编程和实际开发问题解决具有重要意义。
57 15
|
27天前
|
人工智能 自然语言处理 算法
通义灵码助力技术求职:如何成为笔试面试冲刺的“超级助手”
在技术岗位竞争日益激烈的当下,求职季的备战已不仅是知识储备的较量,更是效率与实战能力的比拼。面对海量面试题、复杂算法挑战及快速迭代的技术框架,开发者亟需高效工具辅助突破瓶颈。阿里云推出的智能编码工具通义灵码,凭借其代码生成、优化及智能问答等核心能力,正成为开发者备战求职季的“超级助手”。
|
1月前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
99 23
|
26天前
|
数据采集 存储 网络协议
Java HttpClient 多线程爬虫优化方案
Java HttpClient 多线程爬虫优化方案
|
2月前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
189 14
|
2月前
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
80 13
|
2月前
|
安全 Java 开发者
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。
|
3月前
|
Java 程序员 调度
Java 高级面试技巧:yield() 与 sleep() 方法的使用场景和区别
本文详细解析了 Java 中 `Thread` 类的 `yield()` 和 `sleep()` 方法,解释了它们的作用、区别及为什么是静态方法。`yield()` 让当前线程释放 CPU 时间片,给其他同等优先级线程运行机会,但不保证暂停;`sleep()` 则让线程进入休眠状态,指定时间后继续执行。两者都是静态方法,因为它们影响线程调度机制而非单一线程行为。这些知识点在面试中常被提及,掌握它们有助于更好地应对多线程编程问题。
154 9
|
8月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
5月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!

热门文章

最新文章

下一篇
oss创建bucket