熟悉多线程与并发编程,理解各类锁机制,熟悉JUC并发多线程及线程池,熟练异步编排编码,熟悉Redisson在分布式场景下各类锁的应用场景和并发控制原理。

多线程的状态

各类锁

synchronized

升级过程

偏向锁

jvm启动4秒后开启,因为jvm启动有大量的锁竞争,锁的对象头会记录进来的线程id,若使用为同一线程,则直接放行,若存在竞争则升级为轻量级锁。

轻量级锁

出现竞争时,采用cas方式将对象头复制到线程的栈桢中,若失败则while重试,再次获取锁,达到一定次数升级为重量级锁。

重量级锁

出现竞争时,线程直接阻塞,等待cpu二次调度唤醒,调用了操作系统层面的指令。

实现原理

采用对象的monitor进行进入和退出操作实现,monitor包含

_owner:指向当前锁的持有者线程。

_count:锁进入次数。

_EntryList:锁等待的线程队列。

_WaitSet:调用wait等待队列。

线程进入monitor时,判断_count是否为0,为0直接进入,否则判断_owner是否是自己,是自己直接重入,否则进入等地队列。

锁离开时_count减一,减至0是,是否_owner。

不管是synchronized、ReentrantLock或者是redisson的rlock实现原理差不多。

在等待队列方面,ReentrantLock采用了java的aps队列和cas尝试获取锁,rlock采用了本地jvm的队列。

在线程同步唤醒方面,ReentrantLock采用了unpark和park,rlock采用了基于消息发布订阅的unpark和park。

ReentrantLock

java api 级别锁

lock unlock 配合Condition的await和signal达到和synchronized不同的定点唤醒。

Rlock

在redis中,大key为锁的字符串,hashkey为线程的hash值,v为重入次数。阻塞队列使用Java api层面的队列,同步唤醒采用基于消息发布订阅的park和unpark.

Semaphore

限流,20线程同时到acquire,10会阻塞,等release。

new Semaphore(10);

acquire -1

处理30秒。

release +1

CountDownLatch

多线程事务,做完以后触发调用

new CountDownLatch(3);

new Thread( 一堆事情 countDown)

主线程await,等待3个线程完毕进行执行。

CyclicBarrier

CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> {

System.out.println("All " + parties + " threads have reached the barrier!");

});

new Thread( 一堆事情 await)

等3个线程都await之后,回调CyclicBarrier的方法,同时自动填充CyclicBarrier循环使用。

ReentrantReadWriteLock

readLock 返回读锁、writeLock 返回写锁。

读读只有共享。

AtomicLong AtomicReference (放置自建对象) Cas操作。

AtomicStampedReference 带版本的cas操作,解决aba问题。

LongAdder,分很小的单元进行cas,汇总时累加。
异步编排编码

CompletableFuture.supplyAsync 和runAsync,配合同步阻塞的get进行结果获取。将长任务,并行处理,结果需要相互依赖的任务,串行等待。
线程池

corePoolSize(核心线程数):线程池中保持存活的最小线程数。

maximumPoolSize(最大线程数):线程池允许的最大线程数。当队列满了,并且已创建的线程数小于最大线程数时,会创建新的线程来处理任务。

keepAliveTime(线程空闲时间):当线程数大于核心线程数时,多余的空闲线程在终止前等待新任务的最长时间。

unit(时间单位):keepAliveTime参数的时间单位,如TimeUnit.SECONDS。

workQueue(工作队列):用于存放待执行任务的阻塞队列。常用的队列类型有LinkedBlockingQueue(无界队列),ArrayBlockingQueue(有界队列,一般用这个指定队列)。

threadFactory(线程工厂):用于创建新线程。通过自定义线程工厂,可以设定线程名(一般都会设置)、线程组、优先级、守护线程状态等。

handler(拒绝策略):当队列和线程池都满了,说明线程池处于饱和状态,这时如果再提交任务,就需要拒绝处理。线程池提供了四种拒绝策略:AbortPolicy(直接抛出异常),或者自定义策略接受下来任务往mq放,CallerRunsPolicy(主线程执行)。

tomcat 和 java原生的线程池最大区别在于,tomcat 当核心线程数满了以后会直接启动最大线程数处理,达到最大以后在放入阻塞队列。

保证充分利用cpu资源,避免频繁切换,避免线程过大带来浪费。

cpu密集型 核心线程数为cpu核数

io密集型 cpu核数的两倍
ThreadLocal

线程池引用时,每个线程都有自己的一个ThreadLocalMap,key为静态ThreadLocal的弱引用(gc时就去清除),value为set对象的强引用。这便导致了线程池线程的强引用threadlocalmap至set设置的对象这条引用链不断,可能导致取到上一份对象或者该对象一直存在gc不掉。

使用线程私有的堆内存的TLAB实现的。

  1. 应用代码中不再持有 ThreadLocal 的强引用
  2. ThreadLocal 实例仅剩弱引用(来自 Entry 的 key)
  3. GC 时 ThreadLocal 实例被回收,Entry 的 key 变为 null
  4. 但 Entry 本身和 value 仍然被 ThreadLocalMap 引用
  5. 如果线程不终止(如线程池场景),value 永远无法释放

为什么要用弱引用

减少threadlocal对象的泄露风险。

当应用代码结束引用时,map仍旧引用,强引用减少不掉。

若为static修饰的threadlocal,多次set确实会替换原有V,但是依旧释放不了V

相关推荐
鼠鼠我捏,要死了捏7 天前
基于SkyWalking的微服务APM监控实战指南
skywalking·apm·microservices
HeXDev15 天前
【SkyWalking】服务端部署与微服务无侵入接入实战指南
java·微服务·架构·skywalking·链路追踪·微服务治理
HeXDev16 天前
【SkyWalking】配置告警规则并通过 Webhook 推送钉钉通知
skywalking·链路追踪·微服务监控
老三牛擦20 天前
熟悉Docker及Docker-Compose。熟悉Linux常用命令,编写基本Shell脚本。可搭建Jenkins进行Dev/Ops。
skywalking
老三牛擦22 天前
熟练掌握RabbitMQ和Kafka的使用及相关应用场景。异步通知与解耦,流量削峰,配合本地消息表实现事务的最终一致性并解决消息可靠、顺序消费和错误重试等问题
skywalking
500701 个月前
SkyWalking 部署与应用(Windows)
windows·skywalking
递归尽头是星辰2 个月前
SkyWalking架构深度解析:分布式系统监控的利器
skywalking·分布式链路追踪·可观测性·云原生监控·微服务监控
·云扬·2 个月前
【PmHub面试篇】性能监控与分布式追踪利器Skywalking面试专题分析
分布式·面试·skywalking
XMYX-02 个月前
SkyWalking 报错:sw_profile_task 索引缺失问题分析与解决
运维·jenkins·skywalking