JUC多线程-线程池-Thredalocal-CAS-AQS-死锁

简介: JUC多线程-线程池-Thredalocal-CAS-AQS-死锁

一、多线程

1.进程和线程的区别

进程是系统运行的最小单位,一个java程序是一个进程,是互相独立的

线程是独立运行的最小单位,一个java程序可以有多个线程,线程之间可以共享数据

2.死锁的必要条件

互斥:同一资源同一时刻只能由一个程序读取

不可抢占:不能强行剥夺线程占有的资源

请求和保持:请求其他资源的时候对手中的资源保持不放

循环等待:互相等待的资源中,形成闭环

想要预防死锁,只需要破坏一个条件即可,例如使用定时锁

3.Synchronized和lock的区别

  1. Synchronized是关键字,lock是类
  2. syn会自动释放锁,lock需要手动释放
  3. syn是可重入锁、非公平锁、不可中断锁,lock的ReentrantLock是可重入锁,可以是公平锁也可以是非公平锁
  4. syn是jvm层面通过监视器实现的,lock是AQS实现的

4.什么是AQS

深入理解AQS队列同步器原理-从ReentrantLock的非公平独占锁实现来看AQS的原理

AQS是一个抽象类,可以用来构造所和同步类

原理

三个核心组件,一个是state代表加锁状态初始值为0,一个是获取到锁的线程,还有一个阻塞队列。当有线程想要获取锁的时候,会以CAS的形式将state变为1,CAS成功之后将加锁线程设为自己,当其他队列来竞争锁时会判断state是不是0,不是0再判断加锁线程是不是自己,不是自己就把自己放入阻塞队列,这个阻塞队列是双向链表实现的。

可重入锁的原理就是每次加锁时判断一下加锁线程是不是自己,是的话state+1,释放锁的时候就将state-1。当state减到0的时候就去唤醒阻塞队列的第一个线程。

5.slepp()和wait()的区别

  1. sleep是Thread的方法,wait是Object的方法
  2. sleep不会释放锁,wait会释放锁
  3. sleep没有限制,wait需要在同步方法或者同步代码块中执行
  4. sleep自动唤醒,wait需要调用notify或者notifyall唤醒

6.yield()和Join()的区别

yield调用后进入就绪状态

A线程调用B线程的join(),则B执行完前A进入阻塞状态

7.线程池七大参数

核心线程数:线程池中基本线程数量

最大线程数:当阻塞队列满了之后,逐一启动

最大线程的存活时间:当阻塞队列的任务执行完成后,最大线程的回收时间

最大线程的存活时间单位

阻塞队列:当核心线程满了后,后面来的任务进入阻塞队列

线程工厂:用于生产线程

任务拒绝策略:阻塞队列满了之后,拒绝任务的四种策略 (1)抛异常(2)丢弃任务不抛异常(3)打回任务(4)尝试与最老的线程竞争

8.JMM java内存模型

JMM屏蔽了各种硬件和操作系统的内存访问差异,实现java程序在各个平台下都能达到一致的内存访问效果,定义了JVM如何将程序的变量在主存中读取

定义:所有的变量都存在主存中,主存是线程共享区域;每个线程都有自己独立的工作内存,线程想要操作变量必须从主存中copy变量到自己的工作区,每个线程的工作内存都是互相隔离的

由于主存与工作内存之间有读写延迟,切读写不属于原子操作,所以会有线程安全问题

9.如何保证并发的三大特性

原子性:一次或多次操作在执行期间不能被其他线程影响

可以通过 synchronized和Lock实现原子性,volatile不能保证原子性。

可见性:当一个线程在工作内存修改了变量,其他线程应该立刻知道

volatile可以解决可见性问题(能否及时可见),不加volatile也可能可见,但不一定及时。synchronized和Lock也可以保证可见性,因为它们可以保证任一时刻只有一个线程能访问共享资源,并在其释放锁之前将修改的变量刷新到内存中。

有序性:JVM对指令的优化会让指令改变执行顺序,有序性是禁止指令重排序的

volatile可以保证可见性,禁止指令重排序(DCL需要添加volatile)。通过内存屏障实现。

10.ThreadLocal原理

为每个线程创建变量副本,不同线程之间不可见,保证线程的安全。每个线程内部维护一个Map,key作为threadloacl实例,value是保存的副本,使用threadlocal会造成内存泄漏,因为key是弱引用,value是强引用,每次gc的时候会回收key,value不会回收,可以每次使用完就删除value或者使用static修饰threadlocal解决内存泄露的问题

11.CAS锁和缺点

CAS可以保证原子性,思想是更新内存的时候判断是否被别人修改过,没有就直接更新。如果被修改了就要重新获取值,直到更新完成为止,缺点是

  1. 只能支持一个变量的原子操作,不能保证代码块的原子性
  2. CAS频繁导致CPU的开销大
  3. ABA问题:ab线程拿到a的值,a因为某种原因阻塞,b将值修改为其他值,又修改回来,如何成功退出,此刻a线程退出阻塞,查看值,发现还是之前的值,a没有发现值被修改了,这就是aba问题,可以通过增加版本号或者时间戳来解决

12.Synchronized锁原理和优化

syn是通过对象头的markwordk来表明监视器的,监视器本质是依赖操作系统的互斥锁实现的。操作系统实现线程切换需要从用户态转为内核态,成本很高,这种锁叫重量级锁,在JDK1.6之后引入了偏向锁、轻量级锁、重量级锁

偏向锁:当一个线程多次访问一段代码,都没有其他线程竞争,会获得偏向锁,偏向这段代码

轻量级锁:当锁是偏向锁的时候,有另外一个线程访问,会升级为轻量级锁,线程通过CAS获取锁,不会阻塞,提高性能

重量级锁:轻量级锁自旋一段时间还没有获取到锁,会升级为重量级锁,来竞争的线程都会阻塞,性能低下

注意:锁升级不是直接升级,需要经过自旋操作,只能升级不会降级

13. volatile 是什么?可以保证有序性吗?

一旦一个变量被volatile修饰,那么就具有两层语义

1)保证其他线程对这个操作的可见性,volatile修饰的变量被修改之后会被强制写入主存,其他线程立即可见

2)禁止指令重排序,局部有序性(前面的已经操作完成,后面的不能到volatiel前面来)

14.Thread 类中的start() 和 run() 方法有什么区别?

start()内部调用了run()方法,如果直接调用run那么还是单线程,调用start才是多线程

相关文章
|
14天前
|
存储 缓存 Java
什么是线程池?从底层源码入手,深度解析线程池的工作原理
本文从底层源码入手,深度解析ThreadPoolExecutor底层源码,包括其核心字段、内部类和重要方法,另外对Executors工具类下的四种自带线程池源码进行解释。 阅读本文后,可以对线程池的工作原理、七大参数、生命周期、拒绝策略等内容拥有更深入的认识。
什么是线程池?从底层源码入手,深度解析线程池的工作原理
|
7天前
|
Java Spring
spring多线程实现+合理设置最大线程数和核心线程数
本文介绍了手动设置线程池时的最大线程数和核心线程数配置方法,建议根据CPU核数及程序类型(CPU密集型或IO密集型)来合理设定。对于IO密集型,核心线程数设为CPU核数的两倍;CPU密集型则设为CPU核数加一。此外,还讨论了`maxPoolSize`、`keepAliveTime`、`allowCoreThreadTimeout`和`queueCapacity`等参数的设置策略,以确保线程池高效稳定运行。
54 10
spring多线程实现+合理设置最大线程数和核心线程数
|
15天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
28 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
14天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
25天前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
101 6
【Java学习】多线程&JUC万字超详解
|
3天前
|
Java
直接拿来用:进程&进程池&线程&线程池
直接拿来用:进程&进程池&线程&线程池
|
1天前
|
Python
5-5|python开启多线程入口必须在main,从python线程(而不是main线程)启动pyQt线程有什么坏处?...
5-5|python开启多线程入口必须在main,从python线程(而不是main线程)启动pyQt线程有什么坏处?...
|
17天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
37 10
|
24天前
|
存储 Ubuntu Linux
C语言 多线程编程(1) 初识线程和条件变量
本文档详细介绍了多线程的概念、相关命令及线程的操作方法。首先解释了线程的定义及其与进程的关系,接着对比了线程与进程的区别。随后介绍了如何在 Linux 系统中使用 `pidstat`、`top` 和 `ps` 命令查看线程信息。文档还探讨了多进程和多线程模式各自的优缺点及适用场景,并详细讲解了如何使用 POSIX 线程库创建、退出、等待和取消线程。此外,还介绍了线程分离的概念和方法,并提供了多个示例代码帮助理解。最后,深入探讨了线程间的通讯机制、互斥锁和条件变量的使用,通过具体示例展示了如何实现生产者与消费者的同步模型。
|
25天前
|
监控 Java
线程池中线程异常后:销毁还是复用?技术深度剖析
在并发编程中,线程池作为一种高效利用系统资源的工具,被广泛用于处理大量并发任务。然而,当线程池中的线程在执行任务时遇到异常,如何妥善处理这些异常线程成为了一个值得深入探讨的话题。本文将围绕“线程池中线程异常后:销毁还是复用?”这一主题,分享一些实践经验和理论思考。
44 3