1、多线程原理与实践 《Java高并发核心编程 卷二》读书笔记

第一章:多线程原理和实践


本文章是 极致经典《Java高并发核心编程》卷二的读书笔记,很不错的一本书

  1. 进程和线程
    1. 进程的原理
    2. 线程的原理
    3. 进程和线程的区别
  2. 线程的核心原理
    1. 线程的调度和时间片
    2. 线程的优先级
    3. 线程的生命周期
  3. 创建线程的4种方式
    1. 创建线程的方式一:继承Thread类
    2. 创建线程的方式一:实现Runnable接口创建线程目标类
    3. 创建线程的方式三:使用Callable和FutureTask创建线程
    4. 创建线程的方式三:通过线程池创建线程

1.进程和线程

​ CPU承担了计算机的所有计算任务,内存资源承担了运行时数据的保存任务,外存资源承担了数据外部永久存储的任务。其中任务的调度、资源的分配全由操作系统来统领。应用程序以进程的形式运行于OS上,享受着OS提供的服务

1.1进程的基本原理

请添加图片描述

  • 进程组成的三部分:程序段、数据段和进程控制块(PCB)
    • 程序段,即代码段,是进程的程序指令在内存中的位置,包含需要执行的指令集合
    • 数据段:进程的操作数据在内存的位置
    • PCB由描述信息和控制信息,是进程的唯一标识

什么是Java程序的进程?

Java编写的程序都运行在JVM中,每当使用java命令启动一个java应用程序,就会启动一个JVM进程。在这个JVM进程中,所有java程序代码都是以线程形式运行。

JVM首先找到程序的入口main()方法,然后运行main()方法,就会产生一个线程,即主线程和一个GC(垃圾回收线程),当主线程结束,JVM进程也随即结束。

1.2线程的基本原理

早期OS只有进程没有线程,当时的进程作为程序执行和系统进行并发调度的最小单位。随计算机发展,CPU的性能越来越高,进程调度过于笨重,于是进程内部演进出并发调度的线程

为什么在进程中需要线程:同一个进程中,能够更好的地并行处理。Java程序的进程的执行过程就是多线程的执行过程。

请添加图片描述

  • 线程是进程代码段的一次执行,是程序执行的最小单位,也是CPU调度的基本单位
  • 进程则是CPU资源分配的最小单位
  • 一个线程由三部分组成:线程基本信息、程序计数器和栈内存
    • 线程描述信息:线程ID、名称、优先级、线程状态。。
    • 程序计数器:当前线程下一段指令代码的内存地址 (上下文切换时用于场地恢复)
    • 栈内存:线程独占内存,每一个方法的运行都会生成一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法返回地址(方法出口)

1.3进程和线程的区别

  • 线程是“进程代码块”的一次顺序执行流程。一个进程由一个或多个线程组成
  • 线程是Cpu调度的最小单位,也是程序执行的基本单位;进程是CPU资源分配的基本单位。因此线程的划分程度小于进程,使得多线程程序的并发性高
  • 进程间相互独立,但同一个进程内的各个线程之间不完全独立。像线程共享的内存:堆区、方法区、系统资源。。
  • 线程间的切换速度快于进程间的切换速度

2.线程的核心原理

2.1线程的调度和时间片

目前OS中主流的线程调度方式为:基于时间片的方式进行线程调度。线程得到CPU时间片才能执行指令,处于运行状态。没有得到CPU时间片的线程处于就绪状态,等待OS分配下一个CPU时间片。由于时间片的时间非常短,在各个线程之间快速切换,因此表现出多个线程在“同时执行”或“并发执行”

线程的调度模型主要分为:分时调度模型和抢占调度模型 (通过CPU调度算法衍生的调度模型)

  • 分时调度:系统平均分配CPU时间片,所有线程轮流占用CPU
  • 抢占式调度:系统按照优先级分配CPU时间片。优先级高的线程优先获得CPU时间片。优先级高并非一定在优先级低的线程之前先运行,优先级高只能说是抢占到CPU执行权的几率大

java的线程调度模型是抢占式调度模型,所以java的线程都有优先级

CPU调度算法:

  • 先来先服务
  • 短作业优先
  • 优先级调度算法

2.2 线程的优先级

Thread类由对应的属性和方法用于进行优先级相关的操作

优先级高并非一定在优先级低的线程之前先运行,优先级高只能说是抢占到CPU执行权的几率大

private int priority; //该属性保存Thread实例的优先级,默认为5,可设置区间为 1-10 值越大优先级越高
public final void setPriority(int newPriority); //设置优先级
public final int getPriority()//获取优先级

2.3线程的生命周期

关于线程状态转换函数的请参照:

线程生命周期可分为6种状态。Thread类中一个实例属性和一个实例方法专门保存和获取线程状态

private volatile int threadStatus = 0;
public State getState();
    public enum State {
   
        NEW,                     //新建
        RUNNABLE,				//可执行,包括就绪、运行两种状态
        BLOCKED,				//阻塞
        WAITING,				//等待
        TIMED_WAITING,			//限时等待
        TERMINATED;				//终止状态
    }

请添加图片描述

2.3.1NEW新建状态

指线程创建成功但没有调用start()方法,线程一经调用start()方法线程就会从NEW状态转变为Runnable的就绪状态

2.3.2Runnable就绪和运行状态

请添加图片描述

2.3.3TERMINATED 终止状态

转变为TERMINATED终止状态的情况‘

  1. 在run()方法执行完成之后,就变为TERMINATED
  2. 在run()方法执行过程中发生了运行时异常而没有捕获,run()方法将被异常终止
2.3.4TIMED_WAITING限时等待状态

能让线程处于线程限时等待状态,

3.线程的创建方式

3.1继承Thread

3.1.1 Thread类详解

一个线程在Java中使用一个Thread实例来描述。Thread类有用于存储和操作线程的描述信息的属性和方法

请添加图片描述

  • 线程Id : 在JVM进程中唯一,由JVM进行管理
属性:private long tid,
方法:public long getId(),获取线程 Id;线
  • 线程名称
属性:private String name,该属性保存一条 Thread 线程实例的名字。
方法一:public final String getName(),获取线程名称。
方法二:public final void setName(String name),设置线程名称。
方法三:Thread(String threadName),通过此构造方法,给线程设置一个定制化的名字。
  • 线程优先级
  • 是否为守护线程
private boolean daemon = false,该属性保存 Thread 线程实例的守护状态,默认为 false,
表示是普通的用户线程,而不是守护线程。
方法:public final void setDaemon(boolean on),将线程实例标记为守护线程或用户线程,如果
参数值为 true,则将线程实例标记为守护线程。
  • 线程的状态
  • 线程的启动和运行
方法一:public void start(),用来启动一个线程,当调用start方法后,JVM才会开启一个线程来执行用户定义的线程代码逻辑,这个过程,会为新的线程分配需要的资源
方法二:public void run(),作为线程代码逻辑的入口方法。run方法不是由用户来调用,当调用start方法启动一个线程后,只要线程获得了CPU执行时间,便进入了run方法体去执行具体的用户线程代码
  • 获取当前线程
public static Thread currentThread(),该方法是一个非常重要的静态方法,获取当前线程的 Thread 实例对象。
在没有其他的途径获取当前线程的实例对象的时候,可以通过 Thread.currentThread()静态方法获取。
3.1.2通过继承Thread类创建线程

需要执行的步骤:

(1)需要继承Thread类,创建一个新的线程类

(2)同时重写run()方法,将需要并发执行的业务代码编写在run方法中

static class DemoThread extends Thread{
   
        public DemoThread(){
   
            super("DemoThread"+threadNo++);
        }

        @Override
        public void run() {
   
            for (int i = 1; i <=5; i++) {
   
                System.out.println(getName()+"轮次:"+i);
            }
            System.out.println("运行结束");
        }

        public static void main(String[] args) {
   
            Thread thread=null;
            //通过Thread子类创建和启动 2个线程
            for (int i = 0; i < 2; i++) {
   
                thread=new DemoThread();
                thread.start();
            }
            System.out.println("主线程结束");
        }
    }

3.2 实现Runnable接口创建线程目标类

3.2.1关于Thread类和Runnable接口
	public class Thread implements Runnable {
        private Runnable target; //执行目标
        public void run() {
            if(this.target != null) {
                this.target.run(); //调用执行目标的 run()方法
            }
        }
        public Thread(Runnable target) { //包含执行目标的构造器
            init(null, target, "Thread-" + nextThreadNum(), 0);
        } 
    }

在Thread类中的run()方法中,如果target(执行目标)不为空,就执行target属性的run方法而 target 属性是 Thread 类的一个实例属性,并且 target 属性的类型为 Runnable。

Thread类的构造器

//可以为target赋值的
(1)public Thread(Runnable target)
(2)public Thread(Runnable target,String name)
(3)public Thread(ThreadGroup group, Runnable target, String name)

关于Runnable接口

@FunctionalInterface  //函数是接口 只有一个方法的接口
public interface Runnable {
   
 void run();
}
3.2.2通过实现Runnable接口创建线程类

创建步骤:

(1)定义一个新类实现Runnable接口

(2)实现Runnable接口中的run()抽象方法,

(3)通过Thread类创建线程对象:将Runnable实现类实例,作为实参传递给Thread的构造器。将该Runnable实例作为Thread的目标属性target

(4)调用Thread实例的start()方法

public class CreateDemo1 {
   

    static int threadNo=1;
    static class RunTarget implements Runnable{
   
        @Override
        public void run() {
   
            for (int i = 1; i <=5; i++) {
   
                System.out.println(Thread.currentThread().getName()+"轮次:"+i);
            }
            System.out.println("运行结束");
        }
    }

    public static void 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值