一、什么是线程
进程:代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位;
线程:是进程上的一个路径,是进程中的一个实体,线程本身不会独立存在,一个进程中至少有一个线程。
操作系统在进行资源分配时,会把资源分配给进程,CPU除外。
CPU是被分配到线程,线程是CPU分配的基本单位。
当我们启动main函数时,本质上就是启动了一个JVM的进程,而main函数所在的线程就是该进程中的一个线程,也被称为主线程。
一个进程中可以有多个线程,多个线程共享进程的堆和方法区,每个线程维护自己的程序计数器与栈。
线程是CPU执行的基本单位,而CPU使用时间片轮转方式让线程轮询占用,线程维护自身的程序计数器,本质上是字节码行号的指令器,为了CPU调度切换回来时能从正确的地方继续执行。这部分内容可以参考深入理解Java虚拟机(一)。
二、线程的创建与运行
老生常谈,线程的创建方式:
(1)继承Thread类,重写run()方法;
(2)实现Runnable接口,重写run()方法;
(3)使用Callable和Future,重写call()方法;
通过线程池的方式创建线程的方式不在这里说明,后面单独写一篇基于线程池的方式创建线程。
1. 继承Thread类
代码实现如下:
在main函数里创建了一个ThreadDemo的实例,当创建完成threadDemo对象后,线程并没有被启动,直到调用了start()方法后才真正的开始启动线程。当调用start方法后,线程首先进入就绪状态,等待获取CPU资源,当获取到CPU资源后,才真正开始处于运行状态,一旦run方法执行完毕,该线程进入终止状态。
优点:编程简单,在run()方法内获取当前线程直接使用this即可,无需使用Thread.currentThread()方法;
缺点:java的特性,单继承,如果继承了Thread类,就不能再继承其他类。
2. 实现Runnable接口
代码如下:
步骤如下:
(1)实现Runnable接口,并重写run方法,run方法写入线程执行部分;
(2)创建Runnable实例,作为Thread的target对象,Thread才是真正的线程对象;
(3)调用Thread的start方法启动线程。
3. 使用Callable和Future
代码如下:
步骤说明:
(1)实现Callable接口,并重写call方法,call方法即为线程执行体;
(2)生成FutureTask对象,该对象封装了Callable对象的返回值;
(3)创建Thread对象并传入FutureTask对象;
(4)调用start方法启动线程;
(5)通过FutureTask的get方法获取线程执行结果。