1. 高并发,多线程,并发编程等概念的理解(关系和区别)?
高并发是系统运行的一种状态(短时间内遇到大量操作请求)
- 架构层面设计(减少不必要处理:网络请求,数据库操作)
- 优化网络拓扑(减少网络请求时间)
- 系统代码优化,数据结构,算法
- 任务执行的同步,异步操作(用到了多线程)
- jvm调优,垃圾回收策略
- 数据库优化,减少查询时间
- 缓存的使用
- 数据通信,通信方式:tcp/udp, 使用长连接/短链接 nio/bio
多线程是完成任务的一种方法,
通过多线程有助于系统承受高并发状态的实现
2. 同步,异步,阻塞,非阻塞的理解
同步/异步:
- 客户端
- 是否需要等待返回结果
阻塞/非阻塞:
- 服务端
- 是否需要阻塞线程
3. 使用什么方式创建线程更好?(新建多线程的三种方法?对比)
继承Thread类:java不支持多继承,所以扩展性较差
实现Runnable接口: 没有返回值
实现Callable接口:返回Future对象,包含线程信息
应用场景:主线程结束后希望拿到子线程执行的结果
4. 线程状态有哪些?(生命周期)状态转换如何实现?
- 新建-new
- 就绪-runnable
- 等待-waiting
- 计时等待- timed_wait
- 阻塞-blocked
- 终止-terminated
wait和blocked的区别:
wait: java层面控制,wait(),join(),park()的某个线程的时候该线程会呈wait状态
blocked: cpu层面控制,只有出现锁抢占,未获取到锁的线程等待的时候才会blocked
5. 线程基本操作方法
currentThread(), isAlive(), sleep(), wait(),join(),yield()等(自己写demo练一下)
6. sleep(), wait()的区别?
- sleep是Thread类,wait是Object超类的方法
- sleep必须捕获异常,wait不需要捕获
- sleep指定程序暂停执行的时间,让出cpu,但保持监控状态,指定时间到了自动恢复运行,整个过程线程不会释放对象锁;
- wait ,线程会放弃锁,使其他线程可以使用同步控制块或方法,进入等待此对象(调用者)的等待锁定池,只有针对此对象调用notify方法后本线程才会进入对象锁定池准备。
7. start(), run()的区别?
start()用来启动新创建的线程,start()内部调用了run()
run() 是java虚拟机直接调用的,如果直接调用run(),只会在原来的线程中调用,没有新的线程启动。
我们调用start()目的是创建新的线程,进入runnable状态,直接调用run会违背了创建线程的初衷。
8. 一个线程两次调用start()会出现什么情况?
异常:IllegalThreadStateException
只有在new状态下才可以调用start()方法,其他时候不能调用
9. 守护线程Daemon和本地线程区别?
守护线程属于后台线程,非守护线程运行完毕之后,后台线程自动关闭,典型:垃圾回收
- 任何线程通过方法Thread.setDaemon(bool on)设置类型;(在start()之前调用)
- true则把该线程设置为守护线程,反之则为用户线程;
- 大多数情况下,守护线程是jvm自动创建的线程,本地线程即用户线程是用户程序内创建的线程;
10. 什么情况下java程序会产生死锁?如何定位问题?修复?
死锁:
两个进程或线程在执行过程中,因为争夺资源而互相等待,若没有外力作用他们会永久阻塞;
条件:
(资源互斥,循环等待,请求和保持-不释放以获得资源,不剥夺–不强行抢占其他资源)
饥饿:
饥饿:
某个线程/进程长时间等待,不能保证等待时间上界的存在;
因为没有产生的必要条件,可以被消除,所以也称活锁。
通过资源分配图可以检测死锁是否存在,却不能检测是否有进程饿死。
定位:
- 利用jstack,latch 等可以直接定位;
- 类似Jconsole 可以进行有限的死锁检测;
- 查看cpu使用情况,排查出占用cpu时间片最高的线程,打出堆栈信息,排查代码;
- top 查看使用cpu占用率最高的进程
- top -Hp 进程id 查看该进程下cpu占用率较高的线程
- jstack pid 使用jstack命令查看线程具体调用情况;
解决:
绝大多数情况下线上无法解决,只能重启,或线下修正程序本身问题。
11. 什么是多线程的上下文切换?
cpu的控制权由一个已经正在运行的线程切换到另一个就绪并等待获取cpu执行权的线程的过程。