目录
2.为什么这里要用start(),而不是run()来调用run()方法
什么是计算机中的线程?
这篇文章开头我想换一种方式介绍,以一个场景开头:大家使用Windows电脑肯定用过任务管理器,任务管理器是Windows系统中管理应用程序和进程的工具,这里提到了“程序”和“进程”,这两个概念和这个系列文章的主题——线程,有密不可分的关系。
1.程序,进程与线程
可以看到,我目前正在运行的应用有6个,电脑内部的基本性能参数如此,刚才我提到,进程和线程和密不可分的关系,目前进程是304,线程是4903,同时重点关注一下红框选中的部分。我们能不能先有一个小猜想:线程是比进程更小的计算机的基本单元?带着这个问题,我先从专业的角度介绍一下几个概念。
1.程序(Program)
程序是一组指令的集合,也可以理解为计算机中存储的文件,即数据和代码的集合。程序描述的是计算机执行特定任务所需的操作步骤和逻辑。程序通常是以源代码的形式编写,可以通过编译或解释器将其转换为可执行文件或可运行的代码。
程序是一段静态的代码,静态的对象。当我们没有启动程序时,程序只是“静静地躺在硬盘上”,只有在我们编译和运行它是,程序才会产生动态行为。
2.进程(Process)
进程是计算机中运行的程序的实例。它是计算机操作系统资源分配和管理的基本单位。每个进程都有自己独立的内存空间,包含可执行的程序代码、数据、打开的文件、分配的系统资源等。进程之间是相互独立的,一个进程的崩溃或异常不会影响其他进程的执行。
前面我们说到程序是静态的,那么进程就是动态的。
3.线程(Thread)
线程是进程内的执行单元。一个进程可以包含多个线程,这些线程共享进程的资源,包含内存空间、文件句柄等。线程之间可以并发执行,是的程序能够同时处理多个任务。线程之间共享同一进程的上下文、包含内存、打开的文件、信号处理等。
线程是CPU调度和执行的最小单位。下面看一下计算机内存区域和上面我们讲的概念的关系:
一个程序在运行时(成为了进程),虚拟机会为这个程序开创一个独有内存区域,即紫色部分,其中有线程共享区和线程隔离区。不同进程之间是不共享内存的,比如浏览器和记事本,我们要交换数据,要通过复制粘贴的方式。进程之间交换数据和通信的成本很高,比如一些分布式系统。
2.逻辑处理器和内核
1.逻辑处理器(Logical Processor)
逻辑处理器是计算机系统中的执行单元,是执行线程的硬件单位,换句话说,一个进行的多个线程可以在不同的逻辑处理器上同时执行,共享进程的资源、例如内存和文件。
2.内核(Kernel)
计算机操作系统的核心组件,它是位于操作系统最底层的一部分,内核负责管理系统的硬件和软件资源,并提供了访问和控制这些资源的接口,以便其他软件和进程能够运行和硬件进行交互。简单来说:线程是由进程创建的,进程是由内核创建的,进程的资源是由内核分配的。
3.CPU速度
在上手线程小项目前,有必要先提一下CPU时钟的概念。可以看到,我计算机此时的CPU速度是2.54Ghz,它代表2.54千兆赫兹的CPU速度,即每秒执行2.54亿个时钟周期。时钟周期存在于计算机底层,我们在编写程序时很难具体有体现,既然本文是介绍线程,这里我们只需要有个理论基础即可。
用Java实现线程
我们可以借助Java来和计算机交流,实现对线程的观察。通常会有两种方式:
1.继承Thread类
我们可以创建一个继承自Thread类的子类,重写Thread类中的run方法来定义线程的执行逻辑,并通过创建子类的对象,并调用start方法来启动线程。start方法在后面完整代码可以体现。这篇文章我们先着重讲讲这个继承Thread类,这种方式。
public class Test03 extends Thread {
//重写run方法
@Override
public void run(){
for(int i=0;i<=100;i++){
if(i%2==0){
System.out.println("t3:"+i);
}
}
}
}
所以这份代码中实际上就已经有一个完整的线程了,创建线程,其实最终我们要进行后续代码的编写,还是要落到线程的这个对象中,有了对象才能调用方法,调用了方法才能实现我们想要的功能。
接下来是再创建一个类,编写主函数main,其实main也是一个线程。在这个线程中,创建对象,并打印出来我们要的结果。
public class EvenNumberTest {
public static void main(String args[]){
EvenNumber evenNumber=new EvenNumber();
evenNumber.start();
}
}
1.为什么是run()方法,而且是重写了方法
run()方法是Thread这个被继承的父类中写好的方法,既然我们要使用线程,并且要用这个线程来实现具体功能,就要用run()方法,在方法体中写具体的功能代码,而且是要重写方法,实现方法中功能代码的覆盖。
2.为什么这里要用start(),而不是run()来调用run()方法
其实原本我们也是可以采用evenNumber.run()来调用方法的,但是如果采用了run()来调用,这里的run就成为了普通方法,并没有实现多线程的目的。如果我们在打印语句中,都写入这样一条指令:
System.out.println(Thread.currentThread().getName()+i);
即获取当前线程的名称,来看看到底是哪个线程在工作。结果如下:
但如果我们把evenNumber.start();改成evenNumber.run();再来看看结果:
成为普通方法的run,其实并没有真正实质上继承Thread,所以整个运行过程其实是单线程操作。
3.start()这个方法有什么作用?
针对于线程的操作,start()有两个作用,一个是启动线程,另一个作用是实现run()中的功能,即在新的main线程中执行run()中的功能代码。达到代码异步执行的多线程效果。
4.main中的new出对象这步操作是在哪个线程中完成?
EvenNumber evenNumber=new EvenNumber();
evenNumber.run();
在main中的这两步其实是在main中在执行,main中当执行到start()的时候,转入另一个线程evenNumber中,执行其中的run()方法。并且注意,一个已经start()的方法不能再启动一次,如果我们还要完成别的功能,我们再创建一个类,继承Thread,并在main中声明这个新的对象,然后start()启动线程。比如我们要打印出奇数,就再次新建一个类,再启动一次新的线程。
package Plane;
public class OddNumber extends Thread {
//重写run方法
@Override
public void run(){
for(int i=0;i<=100;i++){
if(i%2!=0){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
}
package Plane;
public class EvenNumberTest {
public static void main(String args[]){
EvenNumber evenNumber=new EvenNumber();
evenNumber.start();
OddNumber oddNumber=new OddNumber();
oddNumber.start();
for(int i=0;i<=100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
}
2.实现Runnable接口
这种方式是通过创建一个实现Runnable接口的类,实现其run方法来定义线程的执行逻辑。通过创建该类的对象,同样调用start方法来启动线程。这个用接口的方式,我们放在下篇文章介绍。
class PrimeRun implements Runnable{
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
// 重写run方法
@Override
public void run() {
for(int i = 0; i < 1000; i++){
System.out.println ("线程2:" + i);
}
}
}
同样,这份代码里面也已经有一个完整的线程了。
总结
这篇文章从围绕线程,先从计算机基础到用Java语言实现线程的执行,总结下来,线程是执行计算机任务的最小单位,一个进程可以包含多个线程。程序是指一系列指令的集合,用于完成特定的任务。
在Java中,可以通过继承Thread类或实现Runnable接口来创建线程。线程的启动是通过调用start()方法来实现的,而不是直接调用run()方法。启动线程后,操作系统会负责调度和管理线程的执行。
线程的并发执行能够充分利用多核CPU的计算能力,提高程序的性能和响应能力。可以通过观察线程标识和任务管理器来了解线程之间的交替执行情况。
以上介绍的代码示例展示了如何在Java中创建线程并执行任务。通过创建Thread类的子类或实现Runnable接口的类,并重写run()方法,定义线程的任务逻辑。通过调用start()方法启动线程,实现多线程的并发执行。
总而言之,了解线程、进程和程序的概念是理解计算机并发编程的基础。Java提供了创建和管理线程的机制,通过启动多个线程实现并发执行,提高程序性能。代码示例展示了创建线程的方式和任务执行的过程,加深了对多线程编程的理解。
这个系列会继续更新,我会继续围绕线程,通过代码来应用线程的应用