1. 简要描述 线程 和 进程 的关系,区别以及优缺点
- 进程: 操作系统为程序分配的资源集合,每个进程拥有独立的地址空间。
- 线程: 同一个进程可以包含多个线程,他们共享线程的
地址空间
和资源
。 - 一个进程中可以有多个线程,多个线程共享进程的
堆
和方法区
资源,但是每个线程有自己的程序计数器、虚拟机栈
和本地方法栈
。 - 总的来说,线程是进程划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。线程执行开销小,但不利于资源的管理和保护;而进程正相反。
2. 线程间同步的锁机制
常见的线程同步锁机制包括:锁(如 互斥锁、读写锁)、信号量、条件变量、以及高级别的工具如 CyclicBarrier、CountDownLatch。
3. Java线程 和 操作系统的线程有啥区别
JDK1.2之前,Java线程是一种用户级线程(用户线程),也就是说JVM 自己模拟了多线程的运行,而不依赖操作系统。
由于不能直接使用操作系统提供的功能比如异步IO、只能在一个内核线程上运行,无法利用多核,所以,JDK1.2之后,Java线程基于原生线程实现,也就是说JVM直接使用操作系统原生的内核级线程(内核线程)来实现Java线程,由操作系统内核进行线程的调度和管理
总的来说,现在的Java线程本质其实就是操作系统的线程。
用户线程: 由用户空间程序管理和调度的线程,运行在用户空间(专门给应用程序使用)
内核线程: 由操作系统内核管理和调度的线程,运行在内核空间(只有内核程序可以访问)
区别: 用户线程创建和切换成本低,但不可以利用多核。内核态线程,创建和切换成本高,可以利用多核。
4. 为什么程序计数器、虚拟机栈和本地方法栈是线程私有的呢?为什么堆和方法区是线程共享的呢
程序计数器:线程切换后能恢复到正确的执行位置
虚拟机栈: 存储局部变量、操作数栈、常量池引用
本地方法栈: 存储Java虚拟机执行的Java方法(字节码)
为了保证线程中的布局变量不被其他线程访问到,虚拟机栈
和 本地方法栈
是线程私有的
堆和方法区是所有线程共享的资源,其中堆是进程中最大的一块内存,主要用于存放新创建的对象 (几乎所有对象都在这里分配内存),方法区主要用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
5. 线程的生命周期和状态
线程生命周期内6种状态
-
NEW: 初始状态,线程被创建出来但没有被调用
start()
。 -
RUNNABLE: 运行状态,线程被调用了
start()
等待运行的状态。 -
BLOCKED:阻塞状态,需要等待锁释放。
-
WAITING:等待状态,表示该线程需要等待其他线程做出一些特定动作(通知或中断)。
-
TIME_WAITING:超时等待状态,可以在指定的时间后自行返回而不是像 WAITING 那样一直等待。
-
TERMINATED:终止状态,表示该线程已经运行完毕
线程在生命周期中并不是固定处于某个状态,而是随着代码的执行在不同的状态之间切换。
6. Thread.sleep() & Object.wait()
共同点: 都可以暂停线程的执行
区别:
- sleep() 方法没有释放锁,而wait()释放锁。
wait()
通常被用于线程间交互/通信,sleep()
通常被用于暂停执行。wait()
方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify()
或者notifyAll()
方法。sleep()
方法执行完成后,线程会自动苏醒,或者也可以使用wait(long timeout)
超时后线程会自动苏醒。- sleep()
是
Thread类的静态本地方法,
wait()则是
Object` 类的本地方法。
wait()是让获得对象锁的进程实现等待,会自动释放当前线程占有的对象锁.每个对象(
Object
)都拥有对象锁,既然要释放当前线程占有的对象锁并让其进入 WAITING 状态,自然是要操作对应的对象(Object
)而非当前的线程(Thread
)。
7. 可以直接调用Thread类的run方法吗?
直接执行 run()
方法,会把 run()
方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它。调用 start()
方法方可启动线程并使线程进入就绪状态,当分配到时间片后就可以开始运行了。 start()
会执行线程的相应准备工作,然后自动执行 run()
方法的内容,直接执行 run()
方法的话,不会以多线程的方式执行。
并发 和 并行 的区别
- 并发:两个及两个以上的作业在同一
时间段
内执行。(区间时段内) - 并行:两个及两个以上的作业在同一
时刻
执行。(同时)
同步 和 异步 的区别
- 同步:发出一个调用后,在没有得到结果之前,该调用就不可以返回,一直等待。
- 异步:调用在发出之后,不用等待返回结果,该调用直接返回。
8. 线程不安全
并发编程问题 —> 内存泄露、死锁、线程不安全。
- 线程安全:不管有多少个线程同时访问,都能保证这份数据的正确性和一致性。
- 线程不安全:多个线程同时访问时可能会导致数据混乱、错误或者丢失。
9. 死锁
多个线程同时被阻塞,它们中的一个或者全部都在等在某个资源被释放,由于线程被无限期地阻塞,因此程序不可能正常终止。
例子:线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。
如何检测死锁?
-
使用
jmap
、jstack
等命令查看 JVM 线程栈和堆内存的情况。如果有死锁,jstack
的输出中通常会有Found one Java-level deadlock:
的字样,后面会跟着死锁相关的线程信息。另外,实际项目中还可以搭配使用top
、df
、free
等命令查看操作系统的基本情况,出现死锁可能会导致 CPU、内存等资源消耗过高。 -
采用 VisualVM、JConsole 等工具进行排查。
如何预防死锁
- 破坏请求与保持条件:一次性申请所有资源。
- 破坏不剥夺条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
- 破坏循环等待条件:按某一顺序申请资源,反序释放资源。
如何避免死锁
// 未避免死锁
public class DeadLockDemo {
private static Object resource1 = new Object();//资源 1
private static Object resource2 = new Object();//资源 2
public static void main(String[] args) {
new Thread(() -> {
synchronized (resource1) {
System.out.println(Thread.currentThread() + "get resource1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting get resource2");
synchronized (resource2) {
System.out.println(Thread.currentThread() + "get resource2");
}
}
}, "线程 1").start();
new Thread(() -> {
synchronized (resource2) {
System.out.println(Thread.currentThread() + "get resource2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting get resource1");
synchronized (resource1) {
System.out.println(Thread.currentThread() + "get resource1");
}
}
}, "线程 2").start();
}
}
// 避免死锁
new Thread(() -> {
synchronized (resource1) {
System.out.println(Thread.currentThread() + "get resource1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting get resource2");
synchronized (resource2) {
System.out.println(Thread.currentThread() + "get resource2");
}
}
}, "线程 2").start();
// 无论是哪个线程,都应该首先获取 resource1 的锁,然后再获取 resource2 的锁,则可以避免死锁。
标签:面试题,Java,Thread,编程,死锁,线程,println,方法,resource2
From: https://blog.csdn.net/m0_74119287/article/details/142727443