一.基础篇
1.线程的生命周期及状态
- 新建状态(new):使用new新建了线程对象
- 可运行状态(runnable):调用线程对象的start方法,进入就绪状态,等待获取cpu时间片
- 运行状态(running):线程获取到CPU时间片后开始执行
- 阻塞状态(block):线程因为某种原因暂时放弃对CPU的使用权,停机了执行,进入阻塞状态;直到其进入就绪状态,才有可能获取到cpu时间片进行运行状态
- 死亡状态(dead):线程run方法执行完毕或者异常退出了run方法,则该线程生命周期结束
2.阻塞的三种情况
- 等待阻塞:线程执行过程中执行了wait方法,释放了锁和时间片资源,此线程会被放入等待队列中,进入等待阻塞状态,直至被notify或notifyall后进入就绪状态;
- 同步阻塞:线程获取同步锁synchronized失败,被放入锁池中,进入同步阻塞状态,直到获取锁后进入就绪状态;
- 其它阻塞:sleep阻塞,join阻塞,IO阻塞,这些情况结束后进入就绪状态
3.创建线程的各种方式以及这些方式的特点
- 继承Thread类
//继承Thread类创建线程 缺点:由于Java只能单继承,所以此方法扩展性差,而且不能返回值和抛出异常 优点:但简单易用 public static class Thread1 extends Thread{ @Override public void run() { System.out.println("线程Thread1通过继承Thread创建");; } }
- 实现Runnable接口
//实现Runnable接口创建线程 缺点:不能返回值和抛出异常 优点:接口可以多实现,扩展性好 public static class Thread2 implements Runnable{ @Override public void run() { System.out.println("线程Thread2通过实现Runnable创建");; } }
- 实现Callable接口
//实现Callable创建线程 优点:扩展性好,具有返回值,能抛出异常 缺点:调用时写法略微复杂 public static class Thread3 implements Callable { @Override public Object call() throws Exception { System.out.println("线程Thread3通过实现Callable创建"); return null; } }
Thread与Callable底层上也是通过实现Runnable接口
4.进程与线程的区别
- 线程是进程的一部分,单独的线程无法运行,一个进程至少包含一个线程
- 不同进程间的数据很难共享,同一进程的不同线程的数据较易共享
- 进程所耗资源高于线程
- 进程间不会相互影响,而一个线程挂掉可能导致进程挂掉
- 进程是操作系统进行资源分配的基本单位,线程是cpu任务调度和执行的基本单位
5.并发与并行的区别
- 并发:在同一时间段,多个任务线程工作在一个cpu的一个核上,按时间细分交替执行。由于CPU切换速度极快,从外部看好像是这些任务线程是一起执行的。(例:5个任务线程在1个CPU的1个核上在1s时间内需要执行完任务,每个任务差不多耗时0.2s,然后cpu在每个任务线程间不断切换到最后完成任务,但对于外部环境看起来好像是一起执行的)
- 并行:在同一时间点上,多个任务在工作在多个cpu上或多个核上,是真正意义上的同时执行
二.线程池篇
线程池的创建
三.锁篇
1.sleep与wait的区别
- sleep属于线程方法,wait属于对象方法
- sleep会释放cpu时间片,但不会释放锁;wait会释放cpu时间片和锁
- sleep通常用于线程暂停,wait通常用于线程间交互
- sleep后线程会自动苏醒进入就绪状态,wait则不会自动苏醒,直至其它线程调用同一对象的notify和notifyall方法
2.volatile弱同步机制
- 保证可见性
- 禁止指令重排序
3.synchronized同步锁
4.synchronized同步锁与ReentrantLock可重入锁区别
- synchronized是关键字,ReentrantLock是类
- synchronized只能创建非公平锁,ReentrantLock能创建公平锁与非公平锁
- synchronized自动加锁与释放锁,ReentrantLock手动加锁与释放锁
- synchronized不可响应中断,ReentrantLock可响应中断