1 多线程的介绍
1.1 操作系统的演进过程
批处理操作系统(单道和多道):
计算机能够自动地、成批地处理一个或多个用户的作业。不过单道批处理系统在进行I/O操作的时候,CPU资源是没有被利用的,为了能够更高效的利用cpu资源,多道批处理系统诞生了。
单道批处理操作系统工作图示:
多道(两道)批处理操作系统工作图示:
分时操作系统
批处理系统对于用户需要的计算机系统来说缺少交互性,工作之间的独立性。以及能在预期时间内完成用户需要的任务。分时操作系统产生了,它支持多用户交互式操作系统,每个用户可以通过自己的终端向系统发出各种操作控制命令,完成作业的运行。分时是指把cpu的运行时间分成很短的时间片,按时间片轮流把cpu资源分配给各任务使用。
分时操作系统工作图示:
实时系统:常用于飞机飞行、导弹发射,等场景,这里不做详细介绍。
1.2 线程和进程的关系
介绍完操作系统之后再来看进程和线程。随着计算机硬件的发展,计算机的内存越来越大,cpu数也越来越多。进程和线程为了更好的利用计算机资源自然而然的诞生了。
进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,系统会给每个进程分配独立的内存地址空间,并且每个进程的地址不会相互干扰。对于单核CPU来说,在任意一个时刻,只会有一个任务去执行,只是通过切换时间片的方式完成了并行执行。多个进程之间通过CPU时间片的不断切换交替执行(给人的感觉就是应用程序同时进行的,所以你可以无感知的一边写代码,一边听歌)。
线程
线程是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,每个线程会负责一个独立的子任务,进程在一个时间内只能干一件事情,如果想同时干多件事情的话, 就要把进程中的多个子任务划分到多个线程,利用线程更高效的完成任务。
2 多线程的适合场景
什么情况下需要使用多线程呢?对于我们编写程序来说一般一个主线程就可以处理所有的事情,但是当我们需要对于一些耗时操作做一些优化的时候,可以考虑多线程。
- 需要多次从其他rpc服务取数据,可以考虑利用多线程获取数据,都返回之后再统一处理。
- 需要从数据库中的多个表中取数据,可以考虑利用多线程从表取数据,然后再处理。
- 需要我们需要导入一个较大的表格数据的时候,可以考虑利用多线程分批导入数据等等。
使用多线程时需要我们注意⚠️ 因为多线程之间执行是无序的,我们需要确定并行的任务之间是独立的才可以。如果两个线程之间的任务不是独立的,需要利用线程安全的变量做线程之前的通信。
3 多线程的实践(java)
3.1 我们先来看一下如何创建一个线程
通过继承Thread类
public class TestThread extends Thread {
public TestThread() {
}
@Override
public void run() {
System.out.print("当前执行线程是:" + this.getName() + " ");
}
public static void main(String[] args) {
System.out.println("当前的线程是" + Thread.currentThread().getName());
TestThread testThread1 = new TestThread();
TestThread testThread2 = new TestThread();
testThread1.start();
testThread2.start();
}
}
实现Runnable接口
public class TestRunnable implements Runnable {
private int count = 10;
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "运行 count= " + count--);
}
}
public static void main(String[] args) {
TestRunnable testRunnable = new TestRunnable();
new Thread(testRunnable, "C").start();
new Thread(testRunnable, "D").start();
}
}
实现Callable接口
import java.util.concurrent.*;
public class TestCallable implements Callable<Integer> {
private final static ExecutorService executor = Executors.newCachedThreadPool();
private Integer number;
public TestCallable(Integer number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
System.out.println("线程number=" + number);
return number;
}
public static void main(String[] args) {
executor.submit(new TestCallable(1));
}
}
从JDK 1.5之后,提供了Calla