熟悉多线程与并发编程,理解各类锁机制,熟悉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

相关推荐
5007011 天前
SkyWalking 部署与应用(Windows)
windows·skywalking
递归尽头是星辰1 个月前
SkyWalking架构深度解析:分布式系统监控的利器
skywalking·分布式链路追踪·可观测性·云原生监控·微服务监控
·云扬·1 个月前
【PmHub面试篇】性能监控与分布式追踪利器Skywalking面试专题分析
分布式·面试·skywalking
XMYX-01 个月前
SkyWalking 报错:sw_profile_task 索引缺失问题分析与解决
运维·jenkins·skywalking
神雕大侠mu1 个月前
skywalking使用教程
skywalking
杰克逊的日记2 个月前
SkyWalking的工作原理和搭建过程
云原生·监控·skywalking
醇氧2 个月前
【skywalking】index“:“skywalking_metrics-all“},“status“:404}
skywalking
·云扬·2 个月前
【PmHub后端篇】Skywalking:性能监控与分布式追踪的利器
分布式·skywalking
大G哥2 个月前
【微服务】SpringBoot制作Docker镜像接入SkyWalking详解
spring boot·docker·微服务·架构·skywalking