线程基本概念和创建

发布于:2022-12-27 ⋅ 阅读:(133) ⋅ 点赞:(0)

线程的概念

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

线程的意义

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

并发编程在狭义上指的是一个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类的属性方法

属性 获取方法
ID getId()
名称 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();

通过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());

匿名内部类

//继承Thread类 
Thread t = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(1);
                }
            }
        };

lambda表达式

Thread t = new Thread(() -> {
    for (int i = 0; i < 10; i++) {
        System.out.println(1);
        }
    }

多线程的缺点

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

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

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


网站公告

今日签到

点亮在社区的每一天
去签到