前言
主要描述线程的一些相关概念和易混淆的知识点
这部分内容也是面试官经常爱问的问题
具体这部分的详细知识可看我之前的文章
以及这篇操作系统的相关面试题
【操作系统】常见面试题集锦(全)
1. 什么是线程
线程是⼀个⽐进程更⼩的执⾏单位。⼀个进程在其执⾏的过程中可以产⽣多个线程,与进程不同的是同类的多个线程共享进程的堆和⽅法区资源,但每个线程有⾃⼰的==程序计数器、虚拟机栈和本地⽅法栈==
补充:
- 程序计数器私有主要是为了线程切换后能恢复到正确的执⾏位置
- jvm栈和本地方法栈私有是为了保证线程中的局部变量不被别的线程访问到
2. 并发与并行
- 并发: 同⼀时间段,多个任务都在执⾏ (单位时间内不⼀定同时执⾏);
- 并⾏: 单位时间内,多个任务同时执⾏
3. 线程死锁
多个线程同时被阻塞,它们中的⼀个或者全部都在等待某个资源被释放。由于线程被⽆限期地阻塞,因此程序不可能正常终⽌
产生死锁的四个必要条件:
- 互斥条件:该资源任意⼀个时刻只由⼀个线程占⽤。
- 请求与保持条件:⼀个进程因请求资源⽽阻塞时,对已获得的资源保持不放。
- 不剥夺条件:线程已获得的资源在末使⽤完之前不能被其他线程强⾏剥夺,只有⾃⼰使⽤完毕后才释放资源。
- 循环等待条件:若⼲进程之间形成⼀种头尾相接的循环等待资源关系
避免线程死锁:
- 破坏互斥条件 :这个条件我们没有办法破坏,因为我们⽤锁本来就是想让他们互斥的(临界资源需要互斥访问)。
- 破坏请求与保持条件 :⼀次性申请所有的资源。
- 破坏不剥夺条件 :占⽤部分资源的线程进⼀步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
- 破坏循环等待条件 :靠按序申请资源来预防。按某⼀顺序申请资源,释放资源则反序释放。破坏循环等待条件
4. sleep和wait方法
区别 | wait | sleep |
---|---|---|
不同点 | Object 的方法,会释放锁,调用它的前提是当前线程占有锁(即代码要在 synchronized 中) | Thread 的静态方法,任何对象实例都能调用。不会释放锁,也不需要占用锁 |
相同点 | 都可以被 interrupted 方法中断 | 都可以被 interrupted 方法中断 |
- sleep() ⽅法没有释放锁,⽽ wait() ⽅法释放了锁
- wait() ⽅法被调⽤后,线程不会⾃动苏醒,需要别的线程调⽤同⼀个对象上的 notify() 或 者 notifyAll() ⽅法。
sleep() ⽅法执⾏完成后,线程会⾃动苏醒
5. 调用start()执行run(),为何不是直接调用run()
详情可看我这篇文章
多线程中run()和start()的异同详细分析(全)
直接执⾏ run() ⽅法,会把 run()⽅法当成⼀个 main 线程下的普通⽅法去执⾏,并不会在某个线程中执⾏它,所以这并不是多线程⼯作
6. synchronized 关键字的了解
详情可看我这篇文章
java并发之synchronized详细分析(全)
以保证被它修饰的⽅法或者代码块在任意时刻只能有⼀个线程执⾏。
- synchronized 关键字加到 static 静态⽅法和 synchronized(class) 代码块上都是是给 Class类上锁。
- synchronized 关键字加到实例⽅法上是给对象实例上锁。
7. 线程池
想主要是为了减少每次获取资源的消耗,提⾼对资源的利⽤率
- 降低资源消耗。通过重复利⽤已创建的线程降低线程创建和销毁造成的消耗。
- 提⾼响应速度。当任务到达时,任务可以不需要的等到线程创建就能⽴即执⾏。
- 提⾼线程的可管理性。线程是稀缺资源,如果⽆限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使⽤线程池可以进⾏统⼀的分配,调优和监控
8. 执⾏execute()方法和 submit()方法的区别
- execute() ⽅法⽤于提交不需要返回值的任务,所以⽆法判断任务是否被线程池执⾏成功与否;
- submit() ⽅法⽤于提交需要返回值的任务。线程池会返回⼀个 Future 类型的对象,通过这个 Future 对象可以判断任务是否执⾏成功,并且可以通过 Future 的 get() ⽅法来获取返回值, get() ⽅法会阻塞当前线程直到任务完成
9. 线程的三种创建方式
可看我之前的这篇文章
java线程的三种创建方式详细分析(全)
10. 线程池的四种创建方式
可看我之前的这篇文章
java线程池的四种创建方式详细分析(全)