Zip2001 level
获赞
0
粉丝
0
关注
5
看过 TA
11
门头沟学院
2020
Java
IP属地:江苏
暂未填写个人简介
私信
关注
昨天 04:37
门头沟学院 Java
线程池是用来管理和复用线程的工具,它可以减少线程的创建和销毁开销。1. 线程池的工作流程任务提交 → 核心线程执行 → 任务队列缓存 → 非核心线程执行 → 拒绝策略处理。‼️第一步,线程池通过 submit() 提交任务。第二步,线程池会先创建核心线程来执行任务。第三步,如果核心线程都在忙,任务会被放入任务队列中。第四步,如果任务队列已满,且当前线程数量小于最大线程数,线程池会创建新的线程来处理任务。第五步,如果线程池中的线程数量已经达到最大线程数,且任务队列已满,线程池会执行拒绝策略。2. 线程池的主要参数有哪些线程池有 7 个参数,需要重点关注的有核心线程数、最大线程数、等待队列、拒绝策略。1、corePoolSize:核心线程数,⻓期存活,执行任务的主力。2、maximumPoolSize:线程池允许的最大线程数。3、workQueue:任务队列,存储等待执行的任务。4、handler:拒绝策略,任务超载时的处理方式。也就是线程数达到 maximumPoolSiz,任务队列也满了的时 候,就会触发拒绝策略。5、threadFactory:线程工厂,用于创建线程,可自定义线程名。6、keepAliveTime:非核心线程的存活时间,空闲时间超过该值就销毁。7、unit:keepAliveTime 参数的时间单位。3. 线程池的拒绝策略(也叫饱和策略)有哪些?有四种:AbortPolicy:默认的拒绝策略,会抛 RejectedExecutionException 异常。CallerRunsPolicy:让提交任务的线程自己来执行这个任务,也就是调用 execute 方法的线程。DiscardOldestPolicy:等待队列会丢弃队列中最老的一个任务,也就是队列中等待最久的任务,然后尝试重 新提交被拒绝的任务。DiscardPolicy:丢弃被拒绝的任务,不做任何处理也不抛出异常。
0 点赞 评论 收藏
分享
昨天 00:21
已编辑
门头沟学院 Java
1. 作用:用于修饰方法或代码块,确保在同一时刻只有一个线程能够执行该方法或代码块,实现互斥访问。2. synchronized底层,会不会牵扯到 os 层面会,synchronized 升级为重量级锁时,依赖于操作系统的互斥量——mutex 来实现,mutex 用于保证任何给定时间内,只有一个线程可以执行某一段特定的代码段。3. synchronized如何保证可见性?Step 1: 加锁时,线程必须从主内存中读取数据。Step 2: 释放锁时,线程必须将修改的数据刷回主内存。4. synchronized如何保证有序性?synchronized 通过 JVM 指令 monitorenter 和 monitorexit,来确保加锁代码块内的指令不会被重排。5. synchronized 怎么实现可重入的呢?可重入意味着同一个线程可以多次获得同一个锁,而不会被阻塞。synchronized 之所以支持可重入,是因为 Java 的对象头包含了一个 Mark Word,用于存储对象的状态,包括锁信息。当一个线程获取对象锁时,JVM 会将该线程的 ID 写入 Mark Word,并将锁计数器设为 1。 如果一个线程尝试再次获取已经持有的锁,JVM 会检查 Mark Word 中的线程 ID。如果 ID 匹配,表示的是同一个线程,锁计数器递增。当线程退出同步块时,锁计数器递减。如果计数器值为零,JVM 将锁标记为未持有状态,并清除线程 ID 信息。6. synchronized锁升级过程‼️无锁 ——> 偏向锁(记住你了,熟客专用) ——> 轻量级锁(在线排队) ——> 重量级锁(去休息室等叫号)为了提升 synchronized 的性能,引入了锁升级机制,从低开销的锁逐步升级到高开销的锁,以最大程度减少锁的竞争。没有线程竞争时,就使用低开销的“偏向锁”,此时没有额外的 CAS 操作(同一个线程可以多次获取同一把锁,无需重复加锁);轻度竞争时,使用“轻量级锁”,采用 CAS 自旋,避免线程阻塞;只有在重度竞争时,才使用“重量级锁”,由 Monitor 机制实现,需要线程阻塞。1️⃣ 无锁状态,对象未被锁定,Mark Word 存储对象的哈希码等信息。2️⃣ 偏向锁,当一个线程第一次获取锁时,JVM 会在对象头的 Mark Word 记录这个线程 ID,下次进入 synchronized 时,如果还是同一个线程,可以直接执行,无需额外加锁。3️⃣ 轻量级锁,当多个线程尝试获取锁但不是同一个时段,偏向锁会升级为轻量级锁,等待锁的线程通过 CAS 自 旋避免进入阻塞状态。4️⃣ 重量级锁,如果自旋失败,锁会升级为重量级锁,等待锁的线程会进入阻塞状态,等待监视器 Monitor 进行调度。7. synchronized 和 ReentrantLock 的区别1️⃣ synchronized 由 JVM 内部的 Monitor 机制实现,ReentrantLock基于 AQS 实现。2️⃣ synchronized 可以自动加锁和解锁,ReentrantLock 需要手动 lock() 和 unlock() 。8. 并发量大的情况下,使用 synchronized 还是 ReentrantLock?ReentrantLock,因为:ReentrantLock 提供了超时和公平锁等特性,可以应对更复杂的并发场景。ReentrantLock 允许更细粒度的锁控制,能有效减少锁竞争。ReentrantLock 支持条件变量 Condition,可以实现比 synchronized 更友好的线程间通信机制。9. Lock 了解吗?Lock 是 JUC (Java 并发工具包)中的一个接口,最常用的实现类包括可重入锁 ReentrantLock、读写锁 ReentrantReadWriteLock等。lock 方法会首先尝试通过 CAS 来获取锁。如果当前锁没有被持有,会将锁状态设置为 1,表示锁已被占用。否则,会将当前线程加入到 AQS(抽象队列同步器) 的等待队列中。(CAS获取——>AQS等待)10. AQS 了解多少?AQS 是一个抽象类。AQS 的思想是,如果被请求的共享资源处于空闲状态,则当前线程成功获取锁;否则,将当前线程加入到等待队列 中,当其他线程释放锁时,从等待队列中挑选一个线程,把锁分配给它。11. ReentrantLock 的实现原理ReentrantLock 是基于 AQS 实现的可重入排他锁,使用 CAS 尝试获取锁,失败的话,会进入 CLH 阻塞队列,支持公平锁、非公平锁,可以中断、超时等待。12. 非公平锁和公平锁有什么不同?公平锁意味着在多个线程竞争锁时,获取锁的顺序与线程请求锁的顺序相同,即先来先服务。 非公平锁不保证线程获取锁的顺序,当锁被释放时,任何请求锁的线程都有机会获取锁,而不是按照请求的顺序。如何实现非公平锁?实现非公平锁的关键在于不检查 AQS 等待队列的状态。在线程尝试获取锁(tryAcquire)时,直接通过 CAS 修改 state 变量。如果修改成功,就视为获取锁成功。补充:对象锁的核心:Monitor在 JVM 层面,每个对象都关联着一个 Monitor(监视器)。当线程 A 想要执行被 synchronized 保护的代码时,它必须先去拿这个对象的 Monitor。如果拿到了,锁计数器就从 0 变成 1,这个线程就成了锁的拥有者。其他线程再想拿这把锁,就只能在外面排队等待(Entry Set)。
0 点赞 评论 收藏
分享
03-06 14:49
已编辑
门头沟学院 Java
‼️该变量的所有更新操作对所有线程都是可见的1. 作用:1️⃣ 保证可见性,线程修改volatile变量后,其他线程能立即看到最新值。(可见性)2️⃣ 防止指令重排,volatile变量的写入不会被重排序到它之前的代码。(有序性)2. 如何保证可见性?线程对volatile变量进行写操作时,JVM会在这个变量后插入一个写屏障指令,强制本地内存中的变量值刷新到主内存中。线程对volatile变量进行读操作时,JVM会插入一个读屏障指令,换个指令会强制让本地内存中的变量值失效,重新从主内存中读取最新的值。E.g. 线程A写入volatile变量,该变量被写入主内存中;线程B从本地内存中读取该变量,失效,要从主内存中读取该变量最新的值。3. 如何保证有序性?普通读普通写——————StoreStore  禁止上面的普通写和下面的volatile写的重排——————volatile写——————StoreLoad  禁止下面的volatile读和volatile写的重排——————volatile读——————LoadLoad  禁止上面的volatile读和下面的普通读的重排————————————LoadStore  禁止下面的volatile读和下面的普通写的重排——————普通读普通写4. volatile在汇编语言层面是怎么实现的在汇编语言层面,💡volatile 主要是通过在写操作后添加一个带有 lock 前缀的指令来实现的。这个 lock 前缀有两个核心作用:✨它充当了内存屏障,防止了指令重排序。它会触发硬件的缓存一致性机制(如 MESI),强制将修改后的数据刷入主存,并让其他 CPU 缓存中的该数据失效,从而保证了可见性。5. volatile和synchronized的区别?volatile:该变量的所有更新操作对所有线程都是可见的。synchronized:确保同一时刻只有一个线程能够执行该方法或代码块,实现互斥访问6. volatile和synchronized的实现原理?volatile 的实现:主要靠汇编层面的 lock 前缀指令。它充当了💡内存屏障,强制把修改后的值刷回主存并让其他缓存失效,从而解决可见性和有序性问题。synchronized 的实现:synchronized 依赖 💡JVM 内部的 Monitor 对象来实现线程同步。使用的时候不用手动去 lock 和 unlock,JVM 会自动加锁和解锁。synchronized 加锁代码块时,JVM 会通过 monitorenter 、 monitorexit 两个指令来实现同步:前者表示线程正在尝试获取 lock 对象的 Monitor; 后者表示线程执行完了同步代码块,正在释放锁。使用 javap -c -s -v -l SynchronizedDemo.class 反编译 synchronized 代码块时,就能看到这两个指令。 synchronized 修饰普通方法时,JVM 会通过 ACC_SYNCHRONIZED 标记符来实现同步。
0 点赞 评论 收藏
分享
03-06 12:16
已编辑
门头沟学院 Java
1. ThreadLocal是什么?(作用)是实现线程局部变量的工具类。让每个访问该变量的线程都拥有自己的独立副本,实现线程隔离。2. ThreadLocal的实现原理ThreadLocal 是通过每个线程内部持有一个私有的 ThreadLocalMap (ThreadLocal 类的一个静态内部类)来实现的。当我们调用 set 方法时,实际上是获取当前线程对象,并把数据存入这个线程自带的 Map 中,Key 就是当前的 ThreadLocal 对象。Value可以是任意类型。当调用ThreadLocal的get()方法的时候,会先找到当前线程的ThreadLocalMap,然后再找到对应的值。3. ThreadLocal的优点1️⃣ 每个线程访问的变量副本均独立,避免了共享变量引起的线程安全问题。2️⃣ 实现了变量的线程独占,使变量不需要同步处理,避免资源竞争。3️⃣ 可用于跨方法、跨类时传递上下文数据,不需要在方法间传递参数。4. ThreadLocal的问题问题:内存泄露。ThreadLocalMap 的 Key 是 弱引用,但 Value 是强引用。如果一个线程一直在运行,并且 value 一直指向某个强引用对象,那么这个对象就不会被回收,从而导致内存泄漏。解决方案:使用完 ThreadLocal 后,及时调用 remove() 方法释放内存空间。补充:ThreadLocalMap 内部维护了一个 Entry 类型的数组。每一个 Entry 都是一个键值对:Key:指向 ThreadLocal 对象的弱引用(WeakReference)。Value:具体要存储的变量副本(强引用)什么是弱引用,什么是强引用?强引用:User user = new User("沉默王二") 中,user 就是一个强引用, new User("沉默王二") 就是强引用对象。当 user 被置为 null 时( user = null ), new User("沉默王二") 对象就会被垃圾回收;否则即便是内存空间不足,JVM 也不会回收 new User("沉默王二") 这个强引用对象,宁愿抛出 OutOfMemoryError。弱引用:调用 set 方法后,会将 key = new ThreadLocal<>() 放入 ThreadLocalMap 中,此时的 key 是一个弱引用对象。当 JVM 进行垃圾回收时,如果发现了弱引用对象,就会将其回收。当ThreadLocal与线程池结合使用时,可能会导致内存泄漏的问题。这是因为线程池中的线程在执行完任务后,并不会被销毁,而是重新放入线程池中以供重用。如果在任务执行过程中使用了ThreadLocal,并且没有手动清除其中的数据,那么这些数据会一直保留在线程中。由于线程池中的线程是可重用的,当线程被复用时,原来线程中遗留的ThreadLocal数据依然存在,如果没有及时清理,这些数据会一直占用内存,并且对应的ThreadLocal实例也不会被回收。随着线程池的不断使用,内存中积累的无用ThreadLocal实例和数据也会越来越多,从而导致内存泄漏。(为什么线程池会存在复用问题?因为 ThreadLocal 的生命周期是和线程绑定的。在线程池中,线程在执行完任务后不会销毁,如果前一个任务没有调用 remove() 清理数据,那么下一个任务在复用该线程时,就会读取到上个任务残留的旧数据,造成‘数据污染’。)为了避免这个问题,使用ThreadLocal时需要特别注意在使用完毕后及时清理数据。可以通过在任务执行完毕后手动调用ThreadLocal的remove()方法(在finally中)来清除对应线程的ThreadLocal数据。另外,还可以使用线程池的钩子函数,在线程池中的线程执行完任务后自动清理ThreadLocal数据。#
0 点赞 评论 收藏
分享

创作者周榜

更多
关注他的用户也关注了:
牛客网
牛客网在线编程
牛客网题解
牛客企业服务