在了解线程之前首先要知道什么进程
一.进程和线程
1.1进程
简单来说:进程就是正在运行的程序,它会占用对应的内存区域,由CPU进行执行与计算。
详细来说:进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。
1.2 进程的特点
独立性
进程是系统中独立存在的实体,它可以拥有自己独立的资源,每个进程都拥有自己私有的地址空间,在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间
动态性
进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合,程序加入了时间的概念以后,称为进程,具有自己的生命周期和各种不同的状态,这些概念都是程序所不具备的.
并发性
多个进程可以在单个处理器CPU上并发执行,多个进程之间不会互相影响.
2.线程
线程,又称轻量级进程(Light Weight Process)。 进程中的一条执行路径,也是CPU的基本调度单位。 一个进程由一个或多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量,彼此间完成不同的工作, 同时执行,称为多线程。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。
3.进程和线程的区别
1. 进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位。
2. 一个程序运行后至少有一个进程。
3. 一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义。线程是操作系统可识别的最小执行和调度单位
4. 进程间不能共享数据段地址,但是同进程的线程之间可以。
4.线程的组成
任何一个线程都具有基本的组成部分
CPU时间片: 操作系统(OS)会为每个线程分配执行时间
运行数据:
堆空间: 存储线程需要的对象,多个线程可以共享堆中的数据。
栈空间: 存储线程需使用的局部变量,每个线程都拥有独立的栈。
线程的逻辑代码.
5.线程的特点
1. 线程抢占式执行
效率高
可防止单一线程长时间独占CPU
2. 在单核CPU中,宏观上同时执行,微观上顺序执行
二.线程的创建
1. 继承Thread类,重写run方法
2. 实现Runnable接口
3. 实现Callable接口
1. 继承Thread类
//继承Thread--->重写run方法
public class HelloController extends Thread{
//重写run方法
public void run(){
for (int i=0;i<20;i++){
System.out.println("线程=================="+i);
}
}
}
//创建线程对象--->开启线程---->和主线程进行资源抢夺
public class Demo01 {
public static void main(String[] args) {
//1.创建线程对象
HelloController helloController = new HelloController();
//2.开启线程====>开启时会调用其中的run方法
helloController.start();
//主线程
for (int i=0;i<20;i++){
System.out.println("主线程==========="+i);
}
}
}
2.实现Runnable接口
Thread方法同时实现了Runable接口,重写run方法,相当于将Runnable子类对象作为参数,传入Thread对象
Thread实现类
Runnable接口
实现Runnable接口
代码:
//实现Runnable接口
public class XcRunnAble implements Runnable{
private int ticket = 100;
@Override
public void run() {
for (int i = ticket; i >0 ; i--) {
System.out.println(Thread.currentThread().getName()+"卖了:"+i+"张票");
}
}
}
public class Demo01 {
public static void main(String[] args) {
//1.创建线程对象
XcRunnAble xcRunnAble = new XcRunnAble();
//2.通过Thread实现线程对象
Thread thread = new Thread(xcRunnAble);
//3.开启线程
thread.start();
}
}
三种方式比较:
Thread: 继承方式, 不建议使用, 因为Java是单继承的,继承了Thread就没办法继承其它类了,不够灵活
Runnable: 实现接口,比Thread类更加灵活,没有单继承的限制
Callable: Thread和Runnable都是重写的run()方法并且没有返回值,Callable是重写的call()方法并且有返回值并可以借助FutureTask类来判断线程是否已经执行完毕或者取消线程执行
当线程不需要返回值时使用Runnable,需要返回值时就使用Callable,一般情况下不直接把线程体代码放到Thread类中,一般通过Thread类来启动线程
Thread类是实现Runnable,Callable封装成FutureTask,FutureTask实现RunnableFuture,RunnableFuture继承Runnable,所以Callable也算是一种Runnable,所以三种实现方式本质上都是Runnable实现
3.Callable接口
它和实现Runnable接口差不多,只是该接口种的方法有返回值和异常抛出。
package com.wt.callable;
import java.util.concurrent.*;
/**
* @Author wt
* @Date 2022/7/18 21:06
* @PackageName:com.wt.callable
* @ClassName: Demo01
* @Description: TODO
* @Version 1.0
*/
public class Demo01 {
public static void main(String[] args) {
try {
//创建线程对象
T t = new T();
//创建定长线程池并设置指定长度
ExecutorService executorService = Executors.newFixedThreadPool(5);
//通过线程池使用线程对象
Future<Double> submit = executorService.submit(t);
//获取返回值
Double aDouble = submit.get(); //线程执行完毕,才会把结果返回给该变量
System.out.println(aDouble);
executorService.shutdown();
} catch (InterruptedException e) {
e.printStackTrace(