一、线程是什么?
先搞清楚两个概念
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。(进程是资源分配的最小单位)
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
下图显示了一个线程完整的生命周期。
- 新建状态: 使用
new
关键字和Thread
类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序start()
这个线程。 - 就绪状态: 当线程对象调用了
start()
方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。 - 运行状态: 如果就绪状态的线程获取 CPU 资源,就可以执行
run()
,此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。 - 阻塞状态: 如果一个线程执行了
sleep
(睡眠)、suspend
(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:- 等待阻塞:运行状态中的线程执行
wait()
方法,使线程进入到等待阻塞状态。 - 同步阻塞:线程在获取
synchronized
同步锁失败(因为同步锁被其他线程占用)。 - 其他阻塞:通过调用线程的
sleep()
或join()
发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep()
状态超时,join()
等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
- 等待阻塞:运行状态中的线程执行
- 死亡状态: 一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
多线程是指在一个程序中同时执行多个线程,每个线程独立运行,有自己的执行流和栈。多线程的目的是为了提高程序的执行效率和资源利用率,允许程序同时处理多个任务。
多线程就好比是一家餐馆有多个服务员同时为客人服务。
比如,你在餐馆里吃饭,服务员A正在为你点菜,服务员B却正在给其他客人上菜,服务员C则在收拾餐桌。这些服务员在同一家餐馆里协同工作,但各自负责不同的任务。
在计算机中,多线程也是类似的概念。计算机的中央处理器(CPU)可以同时执行多个线程,就像一家餐馆可以同时有多个服务员在工作一样。
二、为什么要有多线程?
- 提高程序的响应性: 在单线程程序中,如果一个任务阻塞了,整个程序都会停滞。多线程可以使得其他线程继续执行,提高程序的响应性,特别对于需要用户交互的应用更为重要。
- 提高程序的并发性: 多线程允许多个任务并发执行,充分利用多核处理器,提高系统的吞吐量。
- 简化程序设计: 复杂的任务可以分解成多个独立的线程,使得程序的设计更为简洁和灵活。
- 提高资源利用率: 现代计算机通常具有多核处理器,多线程可以充分利用这些处理器资源。
提高效率: 就像在餐馆里,多个服务员同时为不同的客人服务,可以更快地完成任务。
提高响应速度: 如果只有一个服务员,客人可能需要等待很长时间才能被服务。多个服务员可以同时为不同客人提供服务,提高了服务的响应速度。
更好地利用资源: 多线程可以更好地利用计算机的多核处理器,使计算机的性能得到更充分的发挥。
三、多线程的实现方式:
在Java中,实现多线程有两种主要方式:
1、继承 Thread
类:
创建一个类继承自 Thread
类,并重写 run
方法,然后创建该类的实例并调用 start
方法启动线程。示例代码如下:
class MyThread extends Thread {
public void run() {
// 线程执行的代码
}
}
public class Main {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
2、实现 Runnable
接口:
创建一个类实现 Runnable
接口,重写 run
方法,然后创建该类的实例,将其作为参数传递给 Thread 类的构造函数,最后调用 start 方法启动线程。示例代码如下:
class MyRunnable implements Runnable {
public void run() {
// 线程执行的代码
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
这两种方式都允许程序员创建并发执行的线程,但一般来说,推荐使用实现 Runnable 接口的方式,因为这样可以避免类的单继承限制。在实际开发中,还可以使用线程池、Callable 和 Future 接口等更高级的多线程工具。
四、多线程安全
详情参考【多线程】多线程安全,为什么不安全,要怎么做保证其安全,实例
持续更新中,动动小手,点点关注,后续更精彩!