目录
- 1 线程的生命周期及五种基本状态?
- 2 阻塞的情况分三种:
- 3 Java 中用到的线程调度算法是什么?
- 4 请说出与线程同步以及线程调度相关的方法
- 5 sleep() 和 wait() 有什么区别?
- 6 你是如何调用 wait() 方法的?使用 if 块还是循环?为什么?
- 7 为什么线程通信的方法 wait(), notify()和 notifyAll()被定义在 Object 类里?
- 8 为什么 wait(), notify()和 notifyAll()必须在同步方法或者同步块中被调用?
- 9 Thread 类中的 yield 方法有什么作用?
- 10 为什么 Thread 类的 sleep()和 yield ()方法是静态的?
- 11 线程的 sleep()方法和 yield()方法有什么区别?
- 12 如何停止一个正在运行的线程?
- 13 Java 中 interrupt、interrupted 和 isInterrupted 方法的区别?
- 14 什么是阻塞式方法?
- 15 Java 中你怎样唤醒一个阻塞的线程?
- 16 如何在两个线程间共享数据?
1 线程的生命周期及五种基本状态?
1 新建(new):当线程对象被创建时,它处于新建状态。
2 就绪(runnable):线程对象创建后,当调用线程对象的 start()方法,该线程处于就绪状态,等待被线程调度选中,获取cpu的使用权。
3 运行(running):就绪状态(runnable)的线程获得了cpu时间片(timeslice),执行程序代码,则进入运行态。注:就绪状态是进入到运行状态的唯一入口,线程要想进入运行状态执行,首先必须处于就绪状态中;
4 阻塞(block):处于运行状态中的线程需要等待某些事件的发生,比如等待 I/O 操作完成或等待某个锁的释放等,暂时放弃对 CPU的使用权。
此时进入阻塞状态,当等待的事件发生后,线程会被唤醒,进入就绪状态,才有机会再次被 CPU 调用以进入到运行状态。
5 终止(Terminated):当线程完成任务后,或者因异常退出了run()方法,它会进入终止状态。此时,系统会释放线程所占用的资源,并销毁线程的数据结构。
2 阻塞的情况分三种:
1 等待阻塞:运行状态中的线程执行 wait()方法,JVM会把该线程放入等待队列(waitting queue)中,使本线程进入到等待阻塞状态;
2 同步阻塞:线程在获取 synchronized 同步锁失败(因为锁被其它线程所占用),则JVM会把该线程放入锁池(lock pool)中,线程会进入同步阻塞状态;
3 其他阻塞: 通过调用线程的 sleep()或 join()或发出了 I/O 请求时,线程会进入到阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。
当一个线程a调用另一个线程b的 join() 方法时,该线程a会被阻塞,直到被调用的线程b执行完毕后,该线程a再继续执行。
3 Java 中用到的线程调度算法是什么?
线程调度算法是:为多个线程分配 CPU 使用权的特定机制。
常见的调度模型有两种:分时调度模型和抢占式调度模型。
在 Java 中,默认采用的是抢占式调度模型,线程运行时可以被操作系统强制中断,然后将 CPU 时间片分配给更高优先级的线程。
1 分时调度模型(Time Sharing Model) 是指将 CPU 时间分成若干个时间片,每个时间片分配给不同的进程或线程使用,使得每个进程或线程都能够得到一定的执行时间,从而实现多个进程或线程的并发执行。
2 抢占式调度模型(Preemptive Model) 是指操作系统可以强制中断正在执行的进程或线程,将 CPU 时间片分配给更高优先级的进程或线程。当更高优先级的进程或线程出现时,操作系统会立即暂停正在执行的进程或线程,将 CPU 时间片分配给更高优先级的进程或线程执行,从而保证了系统中高优先级进程或线程的及时响应能力。
4 请说出与线程同步以及线程调度相关的方法
1 wait() 方法: 使线程进入等待状态,等待其他线程通知,且释放对象锁,该方法必须在同步代码块中调用。
2 yield() 方法: 使当前线程放弃 CPU 资源,让其他线程执行,但不会释放锁。
3 sleep() 方法: 使当前线程进入睡眠状态,等待一定时间后重新唤醒,但不会释放锁。
4 join() 方法: 使当前线程等待另一个线程执行完毕后再继续执行。
5 notify() 方法: 用于唤醒一个处于等待状态的线程,该方法必须在同步代码块中调用。
6 notifyAll() 方法: 用于唤醒所有处于等待状态的线程,该方法必须在同步代码块中调用。
5 sleep() 和 wait() 有什么区别?
在 Java 中,sleep() 和 wait() 都是用来暂停线程执行的方法,但是它们有以下几个区别:
1 所属类不同: sleep() 方法是 Thread 类的静态方法,可以在任何地方使用;而 wait() 方法是 Object 类的方法,只能在同步代码块中使用。
2 用法不同: sleep() 方法可以指定暂停的时间,线程会暂停指定的时间后自动恢复执行;而 wait() 方法需要等待其他线程调用 notify() 或 notifyAll() 方法来通知等待的线程才能恢复执行。
3 是否释放锁 sleep() 方法不会释放锁;而 wait() 方法会释放锁,其他线程可以获取锁并执行同步代码块。
因此,sleep() 方法主要用于控制线程的执行速度,可以使线程暂停一段时间后继续执行;而 wait() 方法主要用于线程之间的协作,可以让一个线程等待其他线程的通知,或者让其他线程等待当前线程执行完毕后再继续执行。
6 你是如何调用 wait() 方法的?使用 if 块还是循环?为什么?
在调用 wait() 方法时,一般要使用 while 循环来检查条件,而不是使用 if 块。
例如,假设有一个线程a在等待某个条件变为真时调用了 wait() 方法,当条件变为真时,另一个线程b调用了 notify() 方法来唤醒等待的线程a。此时,如果使用 if 块来检查条件,等待的线程a可能会继续执行下去,因为此时条件已经变为真了。但是,如果在等待期间有其他线程c修改了条件,并且该条件再次变为假,那么等待的线程a就会出现问题。
因此,正确的方式是使用 while 循环来检查条件,当等待的线程a被唤醒后,它需要再次检查条件是否满足,如果条件不满足,就继续等待。这样可以避免线程因为条件变化而出现错误。
synchronized (lock) {
while (!condition) {
try {
lock.wait();
} catch (InterruptedException e) {
// 处理异常
}
}
// 执行需要同步的操作
}
在这个示例中,使用了 while 循环来检查条件是否满足,当条件不满足时,线程会继续等待。当条件满足时,线程会向下执行。
如果使用 if 块来检查条件,代码可能会出现如下问题:
synchronized (lock) {
if (!condition) {
try {
lock.wait();
} catch (InterruptedException e) {
// 处理异常
}
}
// 执行需要同步的操作
}
在这个示例中,如果线程被唤醒后,条件已经不满足了,但是线程还是会执行同步操作,这可能会导致程序出现错误。
因此,建议使用 while 循环来检查条件,以确保线程在唤醒后再次检查条件是否满足。
7 为什么线程通信的方法 wait(), notify()和 notifyAll()被定义在 Object 类里?
首先本质上 wait 和 notify 是用于线程之间通信的,wait() 表示当前获取到锁的线程进入休眠状态并释放锁,notify() 表示当前获取到锁的线程去唤醒曾经获取到该锁的 wait() 的休眠线程,这里有一个很关键的点,就是他们通信的基础是获得或者曾经获得的是同一个锁,
而在 Java 中,锁是放在对象上的,所以可以说这两个线程的通信要依赖于这个对象才行,如果不放在对象上,也可以用 Thread.wait( Object o) 把对象传递进去,但是从设计的角度来说,还不如把方法直接放在对象上。
又因为Java中的所有类都继承自Object类,因此将wait()、notify()和notifyAll()方法定义在Object类中可以保证所有的类都有这些方法。
如果将这些方法定义在Thread类中,并不是所有的类都有这些方法,会造成线程间的通信和协调方法不统一。
总之,wait()、notify()和notifyAll()方法的定义是为了方便Java中对象之间的通信和协调,而Object类是所有Java类的基类,因此在这个层次定义这些方法是最为合理和方便的。
8 为什么 wait(), notify()和 notifyAll()必须在同步方法或者同步块中被调用?
当一个线程需要调用一个对象的wait或者notify方法时,首先需要获得该对象的同步锁,而只有进入该对象的同步块中才能获得对象的锁,所以这些方法必须在同步块中被调用。
9 Thread 类中的 yield 方法有什么作用?
yield() 方法用于暂停当前线程的执行,并将 CPU 的使用权让给同一优先级的其他线程。
如果没有其他线程需要执行,那么原来的线程将立即恢复执行。如果有其他线程需要执行,那么当前线程将进入就绪状态,等待调度程序再次分配 CPU 时间。
10 为什么 Thread 类的 sleep()和 yield ()方法是静态的?
因为这些方法不需要访问或修改特定线程对象的状态,而是直接操作当前线程的状态。因此,将它们定义为静态方法可以方便地在任何线程中使用。
11 线程的 sleep()方法和 yield()方法有什么区别?
(1) sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
(2) 线程执行 sleep()方法后转入阻塞(blocked)状态,而执行 yield()方法后转入就绪(ready)状态;
(3)sleep()方法声明抛出 InterruptedException,而 yield()方法没有声明任何异常;
12 如何停止一个正在运行的线程?
1 使用 volatile 标志位停止线程:在线程中使用一个标志位来标识线程是否需要停止,如果标志位被设置为 true,则线程会自行停止执行。
public class ThreadTest extends Thread {
public volatile boolean exit = false;
public void run() {
while (!exit);
}
public static void main(String[] args) throws InterruptedException {
ThreadTest thread = new ThreadTest();
thread.start();
Thread.sleep(5000); // 主线程延迟5秒
thread.exit = true; // 终止线程thread
thread.join();
System.out.println("线程退出!");
}
}
2 使用stop方法强行终止,但是不推荐这个方法,因为stop是过期方法。
public class ThreadTest{
static class Thread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 500000; i++) {
System.out.println("打印的数字"+i);
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread1 thread1 = new Thread1();
thread1.start();
//保证子线程进入运行状态,避免还没运行就被终止
Thread.sleep(100);
//暴力停止子线程
thread1.stop();
}
}
3 使用 interrupt() 方法停止线程:通过调用线程的 interrupt() 方法来请求线程停止执行。实际上只是给线程设置一个中断标志,但是线程依旧会执行。
public class ThreadTest extends Thread {
private boolean flag = true;
@Override
public void run() {
while (flag) {
synchronized (this) {
try {
sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
this.stopThread();
}
}
}
}
public void stopThread() {
System.out.println("线程已经退出。。。");
this.flag = false;
}
public static void main(String[] args) throws InterruptedException {
ThreadTest threadTest = new ThreadTest();
threadTest.start();
System.out.println("线程开始");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadTest.interrupt();
}
}
13 Java 中 interrupt、interrupted 和 isInterrupted 方法的区别?
链接: https://zhuanlan.zhihu.com/p/48566626
1 interrupt:用于中断线程。调用该方法的线程的状态为将被置为”中断”状态。
注意:线程中断仅仅是置线程的中断状态位,不会停止线程。
需要用户自己监视线程的状态为并做处理。
2 调用 interrupted() 方法会清除当前线程的中断状态。
而调用 isInterrupted() 方法不会清除中断状态。
3 在一个线程上调用 interrupted() 方法时,如果线程被中断了,则第一次调用返回 true,而后续调用返回 false。
而在 isInterrupted() 方法中,只要线程被中断了,每次调用都返回 true。
14 什么是阻塞式方法?
阻塞式方法
怎么理解阻塞和非阻塞,同步和非同步
在阻塞式方法执行过程中,调用该方法的线程会被阻塞(block),直到方法执行完成并返回结果,期间不会执行其他任务。
例如,在Java中,ServerSocket的accept()方法会一直等待客户端连接,直到有客户端连接才会返回。
15 Java 中你怎样唤醒一个阻塞的线程?
线程的阻塞与唤醒
Java 并发编程:多线程如何实现阻塞与唤醒