一个Java程序,对应一个进程,一个进程至少一个线程、Java进程至少3个线程、即main()函数对应的主线程、垃圾回收线程、引用处理线程。
目录导航
一、What
1、线程与进程
- 进程:一个程序运行中的实例,操作系统资源分配的基本单位。
- 线程:进程中的一个执行路径、顺序控制流,处理器任务调度和执行的基本单位。
2、并行、串行、并发的对比
- 并行:多个cpu同时处理任务。
- 串行:单个cpu处理多个任务,一个任务执行完成才能执行下一个任务。
- 并发:单个cpu处理多个任务, 每个任务被分配一定的时间片,一个任务执行一段时间后无论完成与否都要切换到下一个任务,等待下一个时间片轮转到后继续执行。
二、Why
1、为什么学习Java多线程
- 掌握并发编程,提高程序对资源的利用率,能更轻松解决多任务处理场景中的问题。
- 面试必问,Java技术中的重要部分。
2、多线程的实际应用场景主要有哪些
- web服务器:Java web应用需要并发处理多个HTTP请求
- 数据库操作:对于数据量较大或者需要多次读写的数据库操作,使用线程可以提高操作效率。
- 定时任务:定时备份、定时清理等。 异步任务,不阻塞非实时性的任务。
- GUI程序:图形用户界面程序需要同时响应用户的多个操作,例如鼠标点击、键盘输入等,使用多线程可以提高程序的响应速度和用户体验。
三、How
1、Java线程的生命周期
Java线程可以分为5种状态: 新建、就绪、运行、阻塞、终止。
也可以细分为7中状态:新建、就绪、运行、阻塞、无限时等待、有限时等待、终止。
- 新建状态(New)
线程实例被创建后、start()方法还未被调用时,处于新建状态。此时JVM已为线程分配了必要的内存。
- 就绪状态(Runnable)
线程对象调用了start()方法后、但还未获得CPU时间片时,就处于就绪状态,此时JVM开辟了一个新的栈空间。就绪状态的线程位于可运行线程池中,等待被线程调度选中,获得CPU的使用权。
- 运行状态(Running)
线程获取到CPU时间片后,就进入运行状态,随后开始执行run()方法中的代码。
- 阻塞状态(Blocked)
当一个线程试图获取一个内部的对象锁(也就是进入一个synchronized块),而该锁被其他线程持有,则该线程进入阻塞状态。阻塞状态的线程在锁被释放时,将会进入就绪状态。
- 无时限等待状态(Waiting)
线程通过调用其自身的wait()方法、join()方法或LockSupport.park()方法,或者通过调用其他线程的join()方法,可以进入等待状态。在等待状态的线程不会被分配CPU时间片,它们只能通过被其他线程显式唤醒进入就绪状态。
- 有时限等待状态(Timed Waiting)
当线程调用了sleep(long ms),wait(long ms),join(long ms),或者LockSupport.parkNanos(), LockSupport.parkUntil()等具有指定等待时间的方法,线程就会进入超时等待状态。当超时时间到达后,线程会自动返回到就绪状态。
- 终止状态(Terminated)
当线程的run()方法执行完毕,或者线程中断,线程就会进入终止状态。
2、Java启动一个新线程的三种方式
1、继承Thread类
class TestThread1 extends Thread{
public TestThread1 () {
}
@Override
public void run() {
// 线程内要执行的代码
}
}
2、实现Runnable接口
Runnable实现类入参Thread。
new Thread(new Runnable() {
@Override
public void run() {
// 线程内要执行的代码
}
},"线程名称1").start();
3、实现Callable接口
Callable实现类入参FutureTask类,最后FutureTask类再入参Thread。
FutureTask task1 = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
// 线程内要执行的代码
int result = 0;
// 返回一个执行结果
return result;
}
});
Thread thread1 = new Thread(task1);
thread1.start()
// 获取线程执行结果
Object obj = task1.get();
3、多线程中的常见问题
待后续专栏内针对每个点扩展学习。
- 线程控制
- 线程的调度
- 线程安全
- 线程竞争
- 线程死锁
- 线程池的使用
参考的连接: