并发编程-基础

1、构建线程方式?

Java中就三种方式:

  • 继承Thread

  • 实现Runnable

  • 实现Callable

本质都是Runnable,其实是一种。

因为继承Thread,间接实现了Runnable

image.png

实现Callable,需要FutureTask做封装,在启动线程时,依然是执行的FutureTask实现Runnable时重写的run方法,在run方法内部,执行的Callable的call方法。

image.png

Runnable和Callable有啥区别,使用场景

答:如果启动子线程执行任务后需要有返回结果,使用Callable。

Runnable的run方法,无法抛出异常,返回结果就是void

Callable的call方法,可以抛出异常,返回结果是Object


2、线程状态?

Java中的Thread类里有枚举,规定了,只有6种

image.png

BLOCKED,WAITING,TIME_WAITING,本质上一样,都是CPU无法分配时间片

BLOCKED:synchronized没拿到锁,阻塞。

WAITING:Unsafe.park(),JUC包下的类在挂起线程时,用的都是这个。

TIMED_WAITING:Unsafe.park(time,unit),默认阻塞这么久,会被自动唤醒,变为RUNNABLE状态

所谓线程挂起,上面三种,都是线程挂起。

但是操作系统方向里的线程,有5种

NEW,READY,RUNNING,BLOCKING,TERMINATED

3、join实现原理?

比如主线程执行t1.join(),主线程需要等待t1执行完之后,再执行。

主线程挂起了一会,等到t1执行完了,主线程被唤醒?

答:Join方法本质是基于synchronized以及wait和notify实现的。直接针对当前线程对象加锁,然后wait挂起线程,wait判断的逻辑是t1线程是否存活。isAlive。如果t1线程存货,WAITING这,如果t1线程凉凉了,isAlive会返回false,不用挂起了,被唤醒。

4、wait和notify?为啥要扔synchronized里?

wait和notify是在持有synchronized锁时,

  • wait方法是让持有锁的线程释放锁资源,并且挂起。

  • notify方法是让持有锁的线程,去唤醒之前执行wait方法挂起的线程,让被唤醒的线程抢锁。

至于为何要在持有synchronized时,才能执行wait和notify,是因为在调整线程存放的队列时,需要持有当前synchronized锁里面的ObjectMonitor,没持有,不让操作。

并且执行wait需要释放锁资源,你没持有锁资源,你释放什么。。。

在ObjectMonitor里,为什么有了cxq还要有EntryList?

答:synchronized到了重量级锁时,会利用CAS拿锁么?!!会!!

image.png

jdk8u/jdk8u/hotspot: 69087d08d473 src/share/vm/runtime/objectMonitor.cpp

cxq队列就是当竞争激烈时,锁持有时间比较长的时候,将线程扔到cxq队列里,挂起。

EntryList的目的:缓冲~

  • 为了避免大量线程追加到cxq队列的头部或者是尾部(默认头部),造成压力过大。

  • 当线程拿锁时,在重量级锁的情况下,也会走CAS,当自旋失败没拿到锁,优先扔到EntryList

重量级锁怎么定义的:查看对象的对象头里的MarkWord里的标识


5、为啥线程停止不推荐使用stop?

stop方法,会直接强制停止线程,不让执行。

用了stop会有什么问题?比如ConcurrentHashMap在执行put方法时,需要先将数据扔到数组或者链表或者红黑树里,扔进去之后,还需要记录元素个数,做+1操作。如果线程执行put后,数据扔进去了,但是没执行+1,导致线程安全问题。

6、线程中断是什么?

线程中断大方向和线程停止相关联。推荐停止线程用interrupt中断。

线程不是你想停,想停就能停。

interrupt只是将线程的中断标记未从默认的false,改为了true。

同时,如果线程处在WAITING或者TIMED_WAITING状态下,会被唤醒。

所以线程停止需要基于判断isInterruptted或者基于线程休眠等操作,触发结束run方法的操作。


主线程启动了子线程,但是主线程凉了,子线程还在么?

这里要看子线程是用户线程,还是守护线程。

如果是用户线程,主线程凉了,不影响子线程。

如果子线程是守护线程,主线程一凉,子线程也凉!

7、多次对一个thread对象执行start会怎样?

抛异常~一个Thread对象,不允许多次执行start方法。

8、项目中为什么使用多线程?

压榨CPU。为了提升效率。

优化某一个接口,单线程处理,500ms,你上了多线程,可能200ms了。

不是所有接口都能上多线程优化,要看业务。

(IO密集)比如业务中有多个没有关联的网络IO的操作,可以上多线程并行处理,减少IO对程序性能带来的影响。

(CPU密集)比如你有一个相对比较大的数据体量做计算或者做数据的封装。可以将比较庞大的数据量做一个切分,让多个线程同时做处理,最后聚合在一起。也可以提升处理效率。

一行数据,1kb - 300多个字

9、为何要采用线程池?

避免每次任务都重新创建线程,任务结束线程还要销毁。线程创建需要重新开辟内存空间,线程结束,需要释放内存空间。线程池有池化技术,可以复用线程,规避频繁创建和销毁。

如果每次来任务,你都直接构建线程处理,这样一来,如果并发大,线程个数直线飙升,对性能没好处。CPU要来回在线程之间切换分配时间片,如果线程太多,资源都浪费在线程切换上了。线程池可以指定好工作线程的个数,别超过限制,超过了,甩你拒绝策略。

有了线程池,可以更好的去监控任务的执行情况。

10、线程池中有空闲的核心线程,投递任务会交给他处理嘛?

首先,线程池的工作流程:

1、走构建核心线程

2、扔阻塞队列

3、构建非核心线程

4、拒绝策略

  • 核心线程空闲时,在干嘛? 在阻塞队列执行take方法,等新任务呢。

  • 如果核心线程个数已经满了,那么任务会扔到阻塞队列,让核心线程处理。

  • 如果核心线程个数没达到要求,会构建新的核心线程,去处理这个任务。

11、核心线程参数设置为0,任务怎么处理?

如果核心线程数设置为0,任务会直接扔到阻塞队列。

但是现在出现了一个场景,阻塞队列有任务,但是没有线程,现在的情况叫任务饥饿。

此时,会构建一个非核心线程,去处理阻塞队列中的任务。而且线程池的最大线程数,最少设置1。

(核心线程数可以设置为0,但是最大线程数最少设置为1)

最大线程数 = 核心线程数 + 非核心线程数。

12、线程池的工作线程如何区别核心和非核心的?怎么区分的?

线程池里,不区分核心线程跟非核心线程。仅仅是在创建的时候,基于有参构造的corePoolSize和maximumPoolSize,做个判断,工作线程最终都是由thread.start启动的。


如果此时线程池核心线程为3个,最大线程为4个。此时工作线程是4个,3个核心,1个非核心满满登登。

此时一个核心线程抛异常,结束了,那么会再创建一个核心线程么?

不会。因为线程池不区分核心和非核心,里面只判断个数,如果有一个工作线程凉了,那还是3个工作线程,满足参数的哟求。


核心线程会结束么?

1、默认不会,但是可以设置参数,然核心线程也有超时时间。allowCoreThreadTimeOut,默认为false,可以设置为true

2、本身不区分核心还是非核心,如果线程在抛出异常等原因,导致结束后,只会根据个数判断,是否满足要求。

13、工作线程如何被回收(存活时间到了)?回收前需要做哪些判断?

1、线程池的工作状态不是RUNNING,可以回收。

2、当前工作线程个数,大于corePoolSize。(有非核心线程)

3、确保不会出现任务饥饿的问题。

回收的方式,就是run方法结束了,线程就销毁了。

14、创建线程池的方式?

两个大方向:

1、使用Executors自带的方式构建(不推荐),线程池参数很多,这种自带的,只提供了修改部分参数的功能,无法完整的掌握线程池的细节。

  • 定长的newfixed

  • 单例的Single

  • 非固定长度的Cached

  • 执行定时任务的Schedule

  • 使用forkJoinPool的WorkStealing

2、手动new ThreadPoolExecutor,自己指定7个核心参数。(推荐)更好把控线程池的情况。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值