• 线程基本概念和创建


    线程的概念

    一个线程就是一个"执行流",一个进程中至少有一个线程,每个线程运行自己的代码块,多个线程会同时运行多段代码。

    线程的意义

    如果把进程比作银行的服务,张三(即用户)来接受服务时必然要经过不同程序,例如身份查询、业务解释、业务办理等等,一个线程就相当于一个工作人员,而多线程就相当于多个工作人员为张三处理事务,每个工作人员同时分工合作,这样就大大提高了工作效率,这也就是所谓的并发编程。

    并发编程在狭义上指的是一个CPU核心运行多个线程,与之对应的还有并行——指n个CPU核心运行n个线程,但是我们常说的并发编程广义上包括了并发和并行。

    并发编程充分利用了多核CPU的性能,从而更能发挥CPU的运算效率。

    同时,线程相比于进程更轻量化,创建和销毁一个线程的速度远大于创建和销毁一个进程的速度。对于一个进程来说,它的创建和销毁有如下步骤:

    创建

    1. 创建PCB
    2. 给进程分配资源(内存、文件),赋给PCB
    3. 把PCB插入链表

    销毁

    1. 把PCB从链表移除
    2. 释放PCB中的资源
    3. 销毁PCB

    其中给进程分配资源和销毁资源极大占用了操作系统的资源,因此效率较低。

    以之前的银行服务为例,如果一个银行同时遇到多个客户就必须开辟多个进程然后销毁,这就大大浪费了时间。

    而一个进程上的多个线程是共享同一份资源的,直到最后一个线程被销毁,这个进程的资源才被释放,所以极大节约了系统资源。

    线程和进程的区分

    操作系统中是通过一组PCB来描述一个进程的,其中每个PCB对应一个线程。

    因此一个进程至少有一个线程,线程是被包含在进程中的。

    由于线程共享资源,一组PCB的内存指针和文件描述符表其实是同一份东西,而状态、上下文、优先级、记账信息则是每个PCB独有的。

    总的来说:

    进程是资源分配的单位

    线程是调度执行的单位

    线程的创建

    Thread类的构造

    java实现了操作多线程的API,想要实现多线程只需要调用API即可,而Thread类就是java提供的API。

    创建Thread类的方法

    方法说明
    Thread()创建线程对象
    Thread(Runnable target)使用 Runnable 对象创建线程对象
    Thread(String name)创建线程对象,并命名
    Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名

    获取Thread类的属性方法

    属性获取方法
    IDgetId()
    名称getName()
    状态getState()
    优先级getPriority()
    是否后台线程isDaemon()
    是否存活isAlive()
    是否被中断isInterrupted()
    • ID 是线程的唯一标识,不同线程不会重复

    • 名称是各种调试工具用到

    • 状态表示线程当前所处的一个情况

    • 优先级高的线程理论上来说更容易被调度到

    • 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。

    • 是否存活,即简单的理解,为 run 方法是否运行结束了

    • 线程的中断问题,下面我们进一步说明

    通过继承Thread对象重写run方法
    class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(1);
            }
        }
    }
    
    MyThread t = new MyThread();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    通过Runnable接口重写run方法传递给Thread对象

    使用Runnable接口可以减少代码的耦合性,即要修改Thread的功能只需要修改对应的Runnable接口或者放入新的Runnable接口

    class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(1);
            }
        }
    }
    
    Thread t = new Thread(new MyRunnable());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    匿名内部类
    //继承Thread类 
    Thread t = new Thread(){
                @Override
                public void run() {
                    for (int i = 0; i < 10; i++) {
                        System.out.println(1);
                    }
                }
            };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    lambda表达式
    Thread t = new Thread(() -> {
        for (int i = 0; i < 10; i++) {
            System.out.println(1);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    多线程的缺点

    线程让程序更有效率的同时也给程序猿带来了很多问题,这些问题往往伴随着它们的优势。

    1. 我们知道多个线程公用同一份资源,同时运行,这就不可避免导致了多个线程同时操作同一份资源的情况,产生一系列如脏读、幻读、不可重复读等问题,为了解决这些问题就必须以效率为代价对线程的权限加以限制,即为线程“上锁“,具体方法会在下篇博客中阐述。

    2. 线程产生的问题难以修复。线程的调度是由操作系统决定的,因此线程执行的顺序是不可预料的,这就给多线程程序的执行带来了太多的变数。所以如果代码产生了问题,由于顺序的不确定性,可能每次运行会产生不同的结果,甚至有可能有的时候运行结果是对的,有的时候会错,这就为调试增加了难度。如果要编写多线程代码,程序猿就必须考虑所有可能的调度情况,保证每种情况都不出现问题。

  • 相关阅读:
    阿里的 24W 字 Java 面试复盘指南!
    基于WOA的VMD超参数优化
    【Android知识笔记】插件化专题(二)
    72-Java的选择排序、二分查找、Lambda表达式
    大顶堆,小顶堆结构添加,移除元素过程以及源码分析(Java)结合LeetCode347(前k个高频元素), 215(第k大的数), 295(数据流的中位数)
    网络舆情监测是干嘛的?
    【HTML5高级第一篇】Web存储 - cookie、localStorage、sessionStorage
    一起来了解XPath吧!
    ChatGLM2-6B模型尝鲜
    【C】栈和队列
  • 原文地址:https://blog.csdn.net/m0_61016568/article/details/126712329