当前位置: 首页 > 后端技术 > Java

Java多线程相关

时间:2023-04-02 01:05:11 Java

1.线程的状态?1.新状态:新线程,没有start2,运行状态(就绪和运行):调用线程的.start方法1)就绪,调用start方法,CPU不分配时间片2)运行,start方法被调用,CPU正在调度3.阻塞状态:在竞争同步锁时,没有获取到线程,线程被挂起4.等待状态:join,wait,(LockSupport)park方法5.超时等待状态:Thread.sleep(long),wait(long),join(long),parkNanos(...)6.死亡状态:run方法结束,或者run方法没有抛出异常。2.线程池核心参数Java提供了直接基于Executors构建线程池的方式使用Executors构建会导致对线程池的控制非常粗暴,必须手动构建线程池publicThreadPoolExecutor(intcorePoolSize,//corethread,intmaximumPoolSize,//最大线程有生存时间longkeepAliveTime,//生存时间,TimeUnitunit,//unit,BlockingQueueworkQueue,//任务队列,ThreadFactorythreadFactory,//线程工厂,为了设置线程的名字,方便后面调试RejectedExecutionHandlerhandler){}//拒绝策略3、线程池执行过程将任务提交到线程池,让线程池中的线程执行任务1.提交后任务到线程池,如果有空闲的核心线程,直接执行。如果没有空闲的核心线程,则尝试创建一个核心线程来执行任务2,如果已经配置了核心线程数,则将任务丢入任务队列,等待核心线程执行完成其他任务后处决我。3、如果任务队列满了,任务放不下,建立最大线程数。4.如果已经构建了最大线程数,则执行拒绝策略。4、线程池中的ctl属性有什么用?ctl是线程池中的一个属性。本质就是int类型的值的高3位描述了线程池的状态,低29位描述了工作线程的数量。线程池执行任务时,需要多次判断线程池的状态,判断任务是否需要执行(以何种方式执行)Low29用于表示线程中现有的工作线程数pool5.线程池的状态?//RUNNING-线程池正常工作,可以处理提交的任务!!!privatestaticfinalintRUNNING=-1<SHUTDOWN,不接收新任务,但会处理线程池中已有的任务包括队列privatestaticfinalintSHUTDOWN=0<STOP,不接收新任务,中断正在处理的任务,不管工作队列任务privatestaticfinalintSTOP=1<workers=newHashSet();8、拒绝策略一:Abort:抛出异常}2:Discard:丢弃,无异常publicvoidrejectedExecution(Runnabler,ThreadPoolExecutore){}3:DiscardOldest:throw丢弃队列时间最长的即将执行的任务publicvoidrejectedExecution(Runnabler,ThreadPoolExecutore)){如果(!e.isShutdown()){e.getQueue().poll();e.执行(r);//再走一遍线程池的执行过程}}4:CallerRuns:调用者处理服务,导致调用者性能急剧下降publicvoidrejectedExecution(Runnabler,ThreadPoolExecutore){if(!e.isShutdown()){r.run();}}9。线程池执行任务前后如何做额外处理try{//执行任务task.run();}catch(Throwablex){抛出=x;抛出新错误(x);}finally{//后增强afterExecute(task,thrown);}}protectedvoidbeforeExecute(Threadt,Runnabler){}protectedvoidafterExecute(Runnabler,Throwablet){}10.如何合理分配线程池的大小在分配线程池的容量时,一定要根据你的业务类型来确定CPU密集型、IO密集型、混合型CPU密集型:更多的CPU在做计算,一直工作IO密集型:更多线程在等待响应Hybrid:各种任务!1.CPU密集型:线程少,推荐:CPU核数+12,IO密集型:线程多,推荐:通用CPU核数*2,(线程等待时间与线程CPU时间之比+1)*CPU数量3.混合型:CPU密集型和IO密集型的操作可以分成两个线程池执行!十一。如果有临界(共享)资源,如何保证线程的安全1.互斥锁:synchronized,Lock(ReentrantLock,ReadWriteLock)2.非阻塞锁:CAS(真的没有锁,底层锁cmpexchg有锁,Java层面没有锁)3.不要使用任何锁:ThreadLocal,volatile在合适的情况下可以使用!ThreadLocal:让多个线程将共享资源复制到本地,不存在多线程操作共享资源volati的问题le:只要不包括对共享数据的并发操作,基本没有问题。12.ThreadLocal到底是什么?ThreadLocal的本质是一个Map。ThreadLocal可以将数据绑定到本地线程。十三、ThreadLocal的内存泄露问题?Java中的四种引用强引用:OOM不清除软引用:内存不足清除弱引用:只要GC清除幻引用:如果拿不到引用,建起来就爽~~解决这个问题,你在使用TheadLocal完成后,执行remove操作14、volatile可见性和禁止指令重排,原子性无法保证!为什么CPU会重新排列指令?CPU会在保证happens-before的前提下对指令进行重新排序,从而提高效率。为了禁止指令重排序,JVM虚拟机提出了一个规范,内存屏障LoadLoadStoreLoadLoadLoadStoreStoreStoreHotSpot虚拟机实现起来非常简单。在两条指令之间,加一个锁来指定实现volatile的效果。当多线程在CPU级别处理共享数据时,将其锁定。lock指令会将CPU内存中的数据操作同步到主存中。15、伪共享(cachelinesharing)问题CPU分为L1、L2、L3内存,CPU内存,效率比在主存中找数据快多了!一般的64位的CPU都有一个cacheline来存放里面的数据。缓存行为64字节。一般的处理方式是让一个业务的数据填满整个缓存行。longl=真实数据。长l1、l2、l3、l4、l5、l6、l7;在JDK1.8中,一般可以使用@Contended注解来实现十六、CAScompare和SwapCAS问题:ABA问题:添加版本号,解决失败次数多,占用CPU资源:不同场景有不同processingschemesSynchronized:处理方案是自适应自旋锁。如果自旋次数过多,线程将被挂起。LongAdder:自增时,如果失败,将失败信息添加到Cell[]中,只能保证一段数据的安全:一段代码不能像synchronized一样加锁,ReentrantLock内部实现了基于CASsynchronized的加锁效果-ReentrantLock:看马老师和黄老师的视频,里面会有系统解释17.ConcurrenthashMap只讲JDK1.8...没有Hash冲突的时候,ConcurrenthashMap尝试插入到数组中的形式中科院。如果出现Hash冲突,此时会锁定当前数组索引位置,以synchronized的形式挂在链表下,如果数组长度达到初始长度的0.75,则数组长度必须加倍,避免链表过长导致查询效率低下。18、ConcurrenHashMap并发扩展时如何保证安全?Node在计算key的hash值时,会特意定义hash值的正常值为正数和负数,具有特殊的意义。如果哈希值为-1,则表示当前节点正在扩容。ConcurrenthashMap每次扩容都会先移动旧数组中table.size-1~table.size-16个索引位置的数据,再迁移其他索引位置的数据。如果一个线程正在插入数据,发现正在扩容,找到未迁移数据的Index位置,帮助第一个扩容线程扩容。初始扩容A:31~16线程B插入数据,发现扩容,帮你迁移数据。索引位置15~0的每个迁移数据都会被标记,表示扩容完成,放置一个ForwardingNode节点,表示扩容完成,扩容时不会应用ConcurrentHashMap的遍历、查询和添加。展开时的线程数,那么为什么展开1个线程时低位值为2,而2个线程呢?如果sizeCtl为-1,表示并发HashMap正在初始化,-N表示正在扩容,所以不得已,这个是1线程扩容的flag。这是-2,-2表示1个线程在扩容,-3表示2个线程在扩容。20.什么是AQSAQS?AQS是JUC包下的并发基类。很多内容都是基于AQS实现的,比如常用的ReentrantLock/Semaphore/CountDownLatch/threadpool。AQS结构?CLH(双向队列)+state(int型变量)在双向队列和CAS的基础上对state进行操作,实现各个JUC下常用的并发内容公平锁:如果AQS队列有Node,就会排队直接,不竞争锁资源是不公平的