1. 创建线程有哪几种方式?(⭐⭐⭐⭐)
创建线程有三种方式,分别是继承Thread类、实现Runnable接口、实现Callable接口。
- 通过继承Thread类来创建并启动线程的步骤如下:
- 定义Thread类的子类,并重写该类的run()方法。
- 创建Thread子类的实例,即创建了线程对象。
- 调用线程对象的start()方法来启动该线程。
- 通过实现Runnable接口来创建并启动线程的步骤如下:
- 定义Runnable接口的实现类,并实现该接口的run()方法。
- 创建Runnable实现类的实例,并将其作为Thread的target来创建Thread对象。
- 调用线程对象的start()方法来启动该线程。
- 通过实现Callable接口来创建并启动线程的步骤如下:
- 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法有返回值。然后再创建Callable实现类的实例。
- 使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
- 使用FutureTask对象作为Thread对象的target创建并启动新线程。
- 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
2. 说说Thread类的常用方法(⭐⭐)
Thread类常用静态方法:
- currentThread():返回当前正在执行的线程;
- interrupted():返回当前执行的线程是否已经被中断;
- sleep(long millis):使当前执行的线程睡眠多少毫秒数;
- yield():使当前执行的线程自愿暂时放弃对处理器的使用权并允许其他线程执行;
Thread类常用实例方法:
- getId():返回该线程的id;
- getName():返回该线程的名字;
- isDaemon():返回该线程是否是守护线程;
- setDaemon(boolean on):将该线程标记为守护线程或用户线程,如果不标记默认是非守护线程;
- setName(String name):设置该线程的名字;
- setPriority(int newPriority):改变该线程的优先级;
- join():等待该线程终止;
3. run()和start()有什么区别?(⭐⭐)
run()方法被称为线程执行体,它的方法体代表了线程需要完成的任务,而start()方法用来启动线程。
调用start()方法启动线程时,系统会把该run()方法当成线程执行体来处理。如果直接调用线程对象的run()方法,系统把线程对象当成一个普通对象,而run()方法也是一个普通方法,而不是线程执行体。
4. 线程是否可以重复启动,会有什么后果?(⭐⭐)
只能对处于新建状态的线程调用start()方法,否则将引发IllegalThreadStateException异常。
原因是当一个线程已经开始执行时,再次启动该线程可能会导致数据不一致或者出现竞态条件,而且重复启动线程也会消耗更多的系统资源,降低系统性能。
5. 介绍一下操作系统 线程的生命周期(⭐⭐)
- 新建:创建新的进程
- 就绪:进程已经获得除CPU时间片以外的任何资源,一旦获得cpu时间片就能立马执行。
- 执行:处于就绪队列中的进程获得了时间片运行进程。
- 阻塞:进程时间片用完进入阻塞队列中等待唤醒。
- 终止:进程执行完毕。
<aside>
💡
在Java中有6种状态:
- 新建态 New 创建新的进程
- 运行态 Runable 合并了就绪态和运行态
- 阻塞态 Blocked 由于没有拿到锁,等待锁
- 等待态 Waiting 调用了wait方法,等待别人唤醒
- 等待时间态 Timed_Waiting 调用了Sleep方法,等待时间到
- 终止态 TERMINATED 线程执行完毕
</aside>
6. 如何实现线程同步?(⭐⭐)
线程同步是指多个线程在执行过程中需要相互协调和处理,以避免竞态条件(race condition)和死锁(deadlock)等问题发生。