Java面试_并发编程_线程基础
- 线程基础
- 线程和进程的区别(出现频率: 3⭐)
- 并行和并发的区别(出现频率: 2⭐)
- 线程的创建(出现频率: 4⭐)
- 线程的状态(出现频率: 4⭐)
- 让线程按顺序执行(出现频率: 3⭐)
- notify()和notifyAll()有什么区别(出现频率: 2⭐)
- wait方法和sleep方法的区别(出现频率: 3⭐)
- 停止正在运行的线程(出现频率: 2⭐)
- 来源
- Gitee地址
线程基础
线程和进程的区别(出现频率: 3⭐)
- 进程是正在运行程序的实例, 进程中包含了线程, 每个线程执行不同的任务
- 不同的进程使用不同的内存空间, 在当前进程下的所有线程可以共享内存空间
- 线程更轻量, 线程上下文切换成本一般上要比进程上下文切换低(上下文切换指的是从一个线程切换到另一个线程)
并行和并发的区别(出现频率: 2⭐)
- 并发是单个CPU同时执行多个线程
- 并行是多个CPU同时执行多个线程
线程的创建(出现频率: 4⭐)
创建线程的方式
- 继承Thread类
- 实现runnable接口
- 实现callable接口
- 线程池创建线程(项目中使用方式)
runnable和callable有什么区别
- Runnable接口的run方法没有返回值
- Callable接口的call方法有返回值, 需要FutureTask获取结果
- Callable接口的call方法允许抛出异常; 而Runnable接口的run方法的异常只能在内部消化, 不能继续上抛
run()和start()有什么区别
- start(): 用来启动线程, 通过该线程调用run方法中所定义的逻辑代码. start方法只能被调用一次
- run(): 正常调用方法, 封装了要被线程执行的代码, 可以被调用多次
线程的状态(出现频率: 4⭐)
线程的状态
- 新建(new)
- 可运行(runnable)
- 阻塞(blocked)
- 等待(waiting)
- 时间等待(timed_waiting)
- 终止(terminated)
线程状态之间的变化
- 创建线程对象是新建状态
- 调用了start()方法变为可执行状态
- 线程获取到了CPU的执行权, 执行结束是终止状态
- 在可执行状态的过程中, 如果没有获取CPU的执行权, 可能会切换为其他状态
- 如果没有获取锁(synchronized或lock)进入阻塞状态, 获得锁再切换为可执行状态
- 如果线程调用了wait()方法进入等待状态, 其他线程调用notify()唤醒后可切换为可执行状态
- 如果线程调用了sleep()方法, 进入计时等待状态, 到时间后可切换为可执行状态
让线程按顺序执行(出现频率: 3⭐)
使用join()方法
public class JoinTest {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("t1");
});
Thread t2 = new Thread(() -> {
try {
// 当t1线程执行完毕后, 线程继续执行
t1.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("t2");
});
Thread t3 = new Thread(() -> {
try {
// 当t2线程执行完毕后, 线程继续执行
t2.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("t3");
});
t1.start();
t2.start();
t3.start();
}
}
notify()和notifyAll()有什么区别(出现频率: 2⭐)
- notifyAll(): 唤醒所有wait的线程
- notify(): 随机唤醒一个wait的线程
public class notifyAndNotifyAllTest {
static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "...waiting...");
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "...被唤醒了...");
}
}, "t1");
Thread t2 = new Thread(() -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "...waiting...");
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "...被唤醒了...");
}
}, "t2");
t1.start();
t2.start();
Thread.sleep(2000);
synchronized (lock) {
// lock.notify(); // 随机唤醒一个wait线程
lock.notifyAll(); // 唤醒所有wait的线程
}
}
}
wait方法和sleep方法的区别(出现频率: 3⭐)
共同点
- wait(), wait(long)和sleep(long)的效果都是让当前线程暂时放弃CPU的使用权, 进入阻塞状态
不同点
- 方法归属不同
- sleep(long)是Thread的静态方法
- wait(), wait(long)都是Object的成员方法, 每个对象都有
- 醒来时机不同
- 执行sleep(long)和wait(long)的线程都会在等待相应毫秒后醒来
- wait(long)和wait()还可以被notify()唤醒, wait()如果不唤醒就一直等待
- 他们都可以被打断唤醒
- 锁特性不同
- wait方法的调用必须先获取wait对象的锁, 而sleep无此限制
- wait方法执行后会释放对象锁, 允许其他线程获得该对象锁
- sleep如果在synchronized代码块中执行, 并不会释放对象锁
停止正在运行的线程(出现频率: 2⭐)
- 使用退出标志, 是线程正常退出, 也就是当run方法完成后线程终止
public class InterruptDemo extends Thread{ volatile boolean flag = false; // 线程执行的退出标记 @Override public void run() { while(!flag) { System.out.println("MyThread...run..."); try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public static void main(String[] args) throws InterruptedException { // 创建MyThread对象 InterruptDemo t1 = new InterruptDemo(); t1.start(); // 主线程休眠6秒 Thread.sleep(6000); // 更改标记为true t1.flag = true; } }
- 使用stop方法强行终止(不推荐, 方法已作废)
- 使用interrupt方法中断线程
- 打断阻塞的线程(sleep, wait, join)的线程, 线程会抛出InterruptedException异常
- 打断正常的线程, 可以根据打断状态来标记是否退出线程
public class InterruptDemo02 { public static void main(String[] args) throws InterruptedException { // // 1. 打断阻塞的线程 // Thread t1 = new Thread(() -> { // System.out.println("t1正在运行... "); // try { // Thread.sleep(5000); // } catch (InterruptedException e) { // throw new RuntimeException(e); // } // }, "t1"); // t1.start(); // Thread.sleep(500); // t1.interrupt(); // System.out.println(t1.isInterrupted ()); // 2. 打断阻塞的线程 Thread t2 = new Thread(() -> { while(true) { Thread current = Thread. currentThread(); boolean interrupted = current. isInterrupted(); if(interrupted){ System.out.println("打断状态 : "+interrupted); break; } } }, "t2"); t2.start(); Thread.sleep(500); t2.interrupt(); System.out.println(t2.isInterrupted()); } }
来源
黑马程序员. 新版Java面试专题视频教程
小林coding. 图解系统-进程管理
Gitee地址
https://gitee.com/Y_cen/java-interview