• java_ 多线程知识笔记(一)


    前言:

    为什么要引入多线程编程

    java引用进程的概念主要为了解决 “并发编程” 的问题,即多个程序一起运行;
    锁紧弄成编程已经可以解决并发编程的问题,并且可以利用CPU多核资源了
    在这里插入图片描述

    因此,线程也就应运而生,线程也叫 “轻量级进程”;
    解决并发编程问题,最终是为了让创建,销毁,调度的速度更快;

    1.如何理解线程

    为什么线程更轻?
    因为线程把创建资源,释放资源的过程都给省下了

    如何理解线程
    看例子:
    上篇博客我们把纺织厂的例子来比喻进程,这篇博客继续使用这个例子:
    在这里插入图片描述
    为什么说线程把创建资源,释放资源的过程都给省下了呢?
    因为 线程A和线程B共享进程里面的资源,就相当于本来是1个人吃10只鸡,多加了一个线程之后就相当于多加了一个人,现在就相当于2个人吃10只鸡;一个进程里面的所有线程都是资源都是共享的,因此创建线程不需要创建和释放资源

    2.进程和线程的关系

    1. 进程和线程的关系是进程包含线程,一个进程最少有一个线程,也可以有多个线程;但是进程里面的线程应该视具体资源而定,不能一味增加,否则会造成程序崩溃
    2. 同一个进程里多个线程之间,共用了进程的同一份资源(内存文件描述符表)
      注意:在同一个进程中,线程1 new的对象,线程2,3都可以直接使用,
      线程1 打开的文件在线程2,3里都可以直接使用
    3. 操作系统实际调度时,是以线程为基本单位调度的,上篇文章说操作系统是以进程为基本单位调度,是一个进程里面只有一个线程的前提下;如果每个进程都有多个线程,每个线程都是独立在CPU上调度的,每个线程也都有自己的执行逻辑 (执行流);
    4. 一个核心执行的是一个线程,如果一个进程有两个线程,那么线程A可能在 核心1上执行,线程B可能在核心2上执行,实际调度过程中,不关心进程,只关心线程;
    5. 一个线程也是通过PCB来描述的,一个进程里面可能对应一个PCB,也可能对应多个PCB,上篇博客里介绍的PCB状态,上下文,优先级,记账信息等都是每个线程所特有的,但是一个进程里的pid,内存指针和文件描述符表都是相同的

    画图解释一下多进程和多线程的关系
    在这里插入图片描述
    第一种方式是多进程,会浪费调大量资源,花费大量成本,去重新申请一个进程
    第二种是多线程方式,直接在一个进程里完成即可,省下很多成本,并且提高了速度

    重点:是不是一味的在一个进程里面申请线程,速度就可以一直提升呢?
    答案肯定是不行的,增加线程数量的时候,也不是一直可以提高速度的,因为纺织厂的空间是有限的,生产线过多,机会拥挤,反而会影响速度;同理,线程太多,核心数目有限,大量的时间反而浪费在线程调度上了

    3.多线程编程

    java中操作多线程,最核心的类是Thread,记住就行了;不要纠结为啥是这个类
    创建线程的目的是为了成立一个独立的执行流(执行一段代码)

    第一种:继承Thread类

    看代码:

    class MyThread extends Thread{
        //重写子类的 run()方法,告诉线程要做什么工作
        @Override
        public void run() {
            while(true){
                System.out.println("==MyThread==");
                //休眠1s,让线程运行速度变慢,方便观看效果
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    
    
    public class Test1 {
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new MyThread();
            thread.start();//真正创建了一个线程
            
            //理解为死循环打印==main==即可
            while(true){
                System.out.println("==main==");
                thread.sleep(1000);//休眠1s,让线程运行速度变慢,方便观看效果
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    在这里插入图片描述
    问题:为什么不在main()中调用run方法?
    如果在main()调用run,此时就没有创建新线程,全程都只有main线程在运行

    在这里插入图片描述
    在这里插入图片描述
    以上就是多线程编程的一个典型例子,即main线程和thread线程同时执行,谁先谁后我们也不确定;


    1. 操作系统调度线程的时候,是==“抢占式执行”==,具体哪个线程在先,哪个线程在后,取决于操作系统调度器,实现具体策略
    2. new Thread对象,并不是创建线程(这里说的线程是 系统内核里的 PCB),调用start才是创建PCB才是货真价实的线程
    3. PCB对应的是线程,一个进程最少有一个PCB,也可以有多个PCB,同一个进程的pid是相同的,不同进程的pid是不同的
    4. PCB是一个数据结构,体现的是进程/线程是如何实现,如何被描述出来的

    java中创建线程的写法有很多种,上面写的是第一种即继承Thread,重写run方法

    第二种:实现Runnable 接口:

    class MyRunnable implements Runnable{
        @Override
        public void run() {
            while(true){
                System.out.println("==MyRunnable==");
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
    
        }
    }
    
    public class Test1 {
    
        public static void main(String[] args) throws InterruptedException {
    
            Runnable runnable = new MyRunnable();//创建一个任务
            Thread thread = new Thread(runnable);//把任务交给线程来处理
            thread.start();//真正创建了一个线程,去执行任务
            
           //理解为死循环打印==main==即可
            while(true){
                System.out.println("==main==");
                sleep(1000);//休眠1s,让线程运行速度变慢,方便观看效果
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    结果:打印如果依旧是 顺序不确定的
    在这里插入图片描述

    为什么要这样写? 目的是为了让线程和线程之间要做的工作分开.解耦合;

    第三种:使用Lambda表达式

    这一种方法是最推荐的,也是最方便的

    public static void main(String[] args) {
    //直接把Lambda任务传给Thread构造方法
            Thread thread = new Thread(() ->{
                System.out.println("==Lambda==");
            });
            thread.start();
            System.out.println("==main==");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.Thread 用法

    1.Thread常见的构造方法

    在这里插入图片描述

        //构造方法举例
    		Thread t1 = new Thread();
            Thread t2 = new Thread(new MyRunnable());
            Thread t3 = new Thread("名字");
            Thread t4 = new Thread(new MyRunnable(), "名字");
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.Thread的几个常见的属性

    在这里插入图片描述

    1. 获取id和名字
     public static void main(String[] args) {
            Thread thread1 = new Thread("第一个线程的名字");
            System.out.println(thread1.getName());//获取线程的名字
            System.out.println(thread1.getId());//获取线程的id
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果:
    在这里插入图片描述


    2.获取线程状态
    getState()方法;

    public static void main(String[] args) throws InterruptedException {
            Thread thread1 = new Thread(() -> {
                System.out.println("==thread1==");
            });
            thread1.setDaemon(true);
            thread1.start();//线程运行
            System.out.println(thread1.getState());//在线程正在运行是获取线程状态,就是runnable
            thread1.join();//等到线程结束
            System.out.println(thread1.getState());//线程结束之后,再获取线程状态就是结束的状态了,TERMINATED
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    运行结果:
    在这里插入图片描述


    1. 看看线程状态是否存活
      isAlive()方法:是判断当前系统里面的 线程 是不是真的存在,并不是判断对象是否真的存在
      在这里插入图片描述

    在这里插入图片描述

     public static void main(String[] args) throws InterruptedException {
            Thread thread1 = new Thread(() -> {
                System.out.println("==thread1==");
            });
          
            System.out.println(thread1.isAlive());//false,并没有创建线程,只是创建了对象,所以是false
            thread1.start();
            System.out.println(thread1.isAlive());//true,创建了线程,并且并没有执行完,所以是true
          
            thread1.join();
           
            System.out.println(thread1.isAlive());//false,线程已经执行完任务并且线程已经销毁,所以线程状态是false
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    如果thread的run还没跑,isAlive()就是false; 如果thread的run正在跑,isAlive()就是true; 如果thread的run跑完了,isAlive()就是false;
    1. 线程中断
      这里的线程中断不是说让正在运行的线程立即终止,而是通知线程你应该停止了,是否真的停止,取决于线程具体的写法
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

    5.等待一个线程

    线程调度是一个随机的过程,等待线程就是了控制两个线程结束的顺序
    在这里插入图片描述

    6.并发和并行

    在这里我们讨论的都是线程,并不是进程
    在这里插入图片描述

  • 相关阅读:
    Python优缺点总结
    信息学奥赛一本通:1410:最大质因子序列
    2022年2000元能玩原神的手机推荐 这3款值得买
    14-1 网络编程和网络通信要素概述
    【opencv学习】opencv基础用法
    vulnhub之momentum
    【ACWing】2725. 数字序列
    java int类型数据溢出问题 时钟 原码 反码 补码 二进制
    小程序如何设置自取规则
    Yjs + Quill 实现文档多人协同编辑器开发(基础+实战)
  • 原文地址:https://blog.csdn.net/weixin_64634186/article/details/128042708