第二天并发篇

一、线程状态
1.新建(New):创建线程对象时
2.就绪(Runnable):线程调用start方法,有执行资格没有执行权
3.运行:当就绪状态时抢到cpu的执行权之后,进入运行状态
4.阻塞(Blocked):当获取锁失败后,进入阻塞状态
5.等待(Waiting):等待被notify()方法唤醒
6.休眠(sleep):休眠一段时间,时间到了之后,进入就绪状态。
7.终止(Terminated):线程死亡

二、线程的七大参数
1.核心线程数(corePoolSize):表示保持活动状态的最小线程数。
2.最大线程数(maximumPoolSize):表示允许创建的最大线程数。
3.空闲时间(keepAliveTime):表示当线程数量超过核心线程数时,多余的空闲线程能够保持存活的时间。
4.阻塞队列(workQueue):表示用于存储等待执行的任务的阻塞队列。
5.单位(unit):表示空闲时间的单位,例如毫秒、秒等。
6.拒绝策略(rejectedExecutionHandler):表示当任务无法提交给线程池执行时采取的策略。
当线程任务进来时,首先尝试创建核心线程进行执行。如果线程池中的线程数量已经达到了核心线程数,
并且阻塞队列也已经满了,那么就会尝试创建新的线程进行执行。
如果线程池中的线程数量已经达到了最大线程数,并且阻塞队列也已经满了,那么就会执行拒绝策略。

示例:假设线程池的核心线程数为 10,最大线程数为 20,阻塞队列容量为 50,
则当有 100 个任务进入线程池时,会有 10 个核心线程立刻执行任务,50 个任务进入阻塞队列,
再有 10 个线程被创建并立即执行剩余的任务。最后还剩下 30 个任务无法执行,就会被拒绝执行。


三、wait 和 sleep的区别?
1.相同点:wait(long) 和 sleep(long) 的效果都是让当前线程暂时放弃 CPU 的使用权,进入阻塞状态
2.不同点:
 2.1方法归属不同:

 sleep是Thread的静态方法,而 wait()都是 Object 的成员方法,每个对象都有
 2.2醒来时间不同:sleep方法是暂停当前线程,相当于休眠一段时间,之后会自动唤醒,而wait()必须被notify 或者notifyall方法唤醒,不然会一直阻塞
 2.3锁特性不同:wait方法的调用必须先获取wait对象的锁,而sleep则无此限制
;wait方法执行后会释放对象锁
 允许其它线程获得该对象锁(我放弃 cpu,但你们还可以用);如果在 synchronized代码块中执行,并不会释放对象锁(我放弃 cpu,你们也用不了)

四、lock与synchronized的区别
1.语法层面:
1.1.synchronized是关键字,源码在jvm中,用c++ 语言实现

1.2.Lock 是接口,源码由jdk 提供,用java语言实现

1.3.使用 synchronized 时,退出同步代码块锁会由jvm自动释放,而使用Lock时,需要手动调用unlock方法释放锁,否则可能会导致死锁等问题。

2.功能层面
相同点:
2.1都可以用来控制多个线程访问共享资源的互斥性。
2.2都支持可重入锁机制,即同一个线程在已经获得锁的情况下能够再次获取该锁。
2.3都支持在发生异常或者代码执行完毕时自动释放锁资源,避免死锁等问题。
不同点:
2.4 Lock 提供了更多的锁机制选择,例如公平锁、非公平锁、可重入锁、读写锁等多种类型;而 synchronized 只有一种类型,即独占锁(排他锁)。
2.5 Lock 应该优先考虑在高并发场景下使用,可以获得更好的性能和灵活性;而 synchronized 关键字则更适合用于简单的线程同步场景,易于使用和维护

五、volatile

1.volatile是java中的关键字,可以保证变量的可见性和禁止指令重排序优化
2.volatile不能保证复合操作的原子性,使用时需要注意
3.使用volatile的情况:多个线程访问共享变量、共享值经常变化、对变量写操作不依赖当前值、实现简单的线程同步机制
4.缺点:内容开销较大、可能导致程序执行效率降低。

六、悲观锁与乐观锁
1.什么是悲观锁和乐观锁?
悲观锁:假设会出现并发冲突,因此在访问共享资源时先加锁,防止其他线程修改数据,
        悲观锁示例:synchronized锁(jvm)和Lock(jdk接口)锁
乐观锁:假设不会出现并发冲突,因此在访问共享资源时先不加锁,在提交更新时检查数据是否被其他线程修改过,
        乐观锁示例:1.基于CAS算法实现:AtomicReference类,内部使用 CPU 提供的原子性操作来实现并发控制2.版本号机制:在数据表中增加一个版本号字段,每次更新时检查版本号是否一致,若不一致则表示数据已被其他线程修改过。
2.悲观锁和乐观锁的优缺点
   悲观锁:优点是可以保证数据的一致性,缺点是在高并发场景下可能会出现资源竞争导致的性能问题。
   乐观锁:优点是可以提高系统的并发能力,缺点是可能会出现数据不一致的情况,需要进行补偿机制或者重试机制等。

3.场景:

        悲观锁适用于写操作较多的场景,例如数据库的 UPDATE、DELETE 操作等,因为这些操作可能会对数据产生破坏性的影响。
        乐观锁适用于读操作较多的场景,例如缓存系统的 GET 操作等,因为这些操作不会对数据产生破坏性的影响。

七、hashmap与concurrentHashmap的区别?
1.线程安全性:
        HashMap是非线程安全的,多线程并发访问会导致数据不一致问题,而ConcurrentHashMap则采用分段锁的思想,在底层数据结构中将数据分为多个段(Segment),
每个段都有自己的,因此可以保证并发访问时的线程安全性
2.并发性:
        由于ConcurrentHashMap在内部使用了分段锁,因此在高并发场景下可以保证更好的性能表现和吞吐量。而HashMap由于没有使用任何锁机制,因此在高并发的环境下容易出现并发冲突和性能瓶颈。
3.内部实现:
        HashMap的内部实现采用的是链表和红黑树等数据结构来解决哈希冲突问题,而ConcurrentHashMap则采用分段锁和CAS算法

注意:虽然ConcurrentHashMap的性能表现更优秀,但是在一些特定的场景下由于锁的竞争,可能会导致出现等待的情况,需根据实际需求选择。

八、THreadLocal原理
  每个 ThreadLocal 实例内部都有一个 Map,用于存储数据。
在默认情况下,这个 Map 是由 ThreadLocal 创建的,
并且存储在当前线程的 ThreadLocalMap 属性中。
当调用 ThreadLocal 的 set() 方法设置值时,实际上是先获取当前线程的 ThreadLocalMap 对象,
然后将 ThreadLocal 实例作为 key,要设置的值作为 value 存入 Map 中。当调用 get() 方法获取值时,实际上是先获取当前线程的 ThreadLocalMap 对象,然后获取 Map 中对应 ThreadLocal 实例的值。
注意点:使用 ThreadLocal 时需要注意内存泄漏问题
如果 ThreadLocal 实例被创建后没有被清理,那么就会存在大量无效的 ThreadLocal 实例和对应的 Map,导致内存占用过高。
可以通过使用 remove() 方法或者将 ThreadLocal 实例声明为静态变量并在不需要时手动清理来避免内存泄漏问题。

猜你喜欢

转载自blog.csdn.net/weixin_71921932/article/details/129907012