多线程基础(1):线程和进程,创建线程的五种方式

一.关于进程和线程

1.什么是进程

进程在操作系统中是这样进行管理的:

  1. 描述:PCB,叫做进程管理块,在创建进程时,建立 PCB,伴随进程运行的全过程,直到进程撤销而撤销.

    (包含 PID,内存指针,文件描述符表,进程的状态,上下文,优先级,记账信息…)实际上这个 PCB 是一个非常大的结构体,属性非常多

  2. 组织:通过双向链表进行组织

虚拟地址空间:操作系统会直接依据真实的内存地址进行划分空间,分配给每一个进程.

2.多线程的概念

一个线程就是一个"执行流".每个线程之间都可以按照顺序执行自己的代码.多个线程之间"同时"执行着多份代码.

3.为什么要有多线程

有人会想,既然已经有进程了,也能够"并发编程",那为啥还需要进程呢?

线程比进程更轻量

对于进程来说,创建进程/销毁进程/调度进程,所需要的开销有点太大了.

进程是资源系统分配的基本单位

创建进程,需要分配资源;销毁进程,就需要释放资源.

如果频繁的创建销毁进程,资源开销势必就大.

于是就引入了线程Thread,也叫"轻量级进程"

4.线程同进程的比较

线程体现在"轻量":

  • 创建线程比创建进程要更高效
  • 销毁线程比销毁进程要更高效
  • 调度线程比调度进程要更高效

对于线程来说, 创建和销毁都是不需要额外的申请和释放资源的,因为他们是共用同一个 进程中的资源.

线程和进程之间的关系:

  • 进程包含线程,一个进程包含一个或多个线程.
    里面的线程共用这个进程创建出来的资源,共享同一个内存空间.

  • 进程是系统分配的基本单元,线程是系统调度的最小单位

5.站在系统内核的角度,来看进程和线程

在 Linux 系统中,线程同样是使用 PCB 来描述的

进程1,对应了一个 PCB,若是在这个进程1中创建一个线程,相当于是再加了一个 PCB.

在操作系统内核角度来看,不分"线程还是进程",只认 PCB

当创建一个进程的时候,就是创建了一个 PCB 出来.
同时这个 PCB 也可以视为是当前进程中已经包含了一个线程了.(一个进程中至少得有一个线程)

在这里插入图片描述

属于同一个进程的线程之间,是共用一份内存空间.同时其他的进程(PCB)使用的是独立的内存空间

6.关于线程调度

系统是以线程为单位进行调度(以 PCB 为单位进行调度)
在这里插入图片描述

7.进程和线程之间的区别和联系:

  • 进程包含线程,一个进程包含一个或多个线程.
  • 每个进程都有独立的内存空间(虚拟地址空间),同一个进程里的多个线程共用一份系统资源(内存资源)
  • 进程是操作系统进行资源分配的基本单位,而线程是系统进行调度的基本单位(PCB为单位进行调度)

二.创建线程

Java标准库中 Thread 类(创建线程的类)对操作系统提供的 API 进行了进一步的抽象和封装

  • 每个线程都是一个独立的执行流
  • 多个线程之间是"并发"执行

1.创建线程方式一:继承Thread,重写run()

  static class MyThread extends Thread{
        @Override
        public void run() {
            while(true){
                try {
                    System.out.println("线程一");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();
        //上面是一个执行流(线程)
        while(true){
            try {
                Thread.sleep(1000);
                System.out.println("主线程");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

2.创建线程方式二:实现Runnable接口,重写run

    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            while(true){
                try {
                    System.out.println("线程一");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyThread());
        t1.start();
        while(true){
            try {
                Thread.sleep(1000);
                System.out.println("主线程");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

3.创建线程的方式三:使用匿名内部类的方式创建Thread的子类,重写run()

    public static void main(String[] args) {
        Thread t1 = new Thread(){
            @Override
            public void run() {
                while(true){
                    try {
                        System.out.println("线程一");
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t1.start();
        while(true){
            try {
                System.out.println("主线程");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

4.创建线程的方式四:使用匿名累不累的方式实现Runnable接口,重写run()

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    try {
                        System.out.println("线程一");
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t1.start();
        while(true){
            try {
                System.out.println("main 线程");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

5.使用 lambda 表达式创建

和第三种道理一样

    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            while(true){
                try {
                    System.out.println("线程一");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        while(true){
            try {
                System.out.println("main 线程");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
//创建了 Thread 子类对象

6.分析线程创建过程,start()和run()的区别

在 java 中,使用 Thread 这个类的对象来表示一个操作系统中的线程.PCB 是在操作系统的内核中,描述线程的.

public static void main(String[] args) {
    Thread t = new Thread(){
        @Override
        public void run() {
            while (true){
                System.out.println("1");
            }
        }
    };
    t.start();
   //t.run();
    //System.out.println("到我了没");
}

对于 start():

当程序运行后,首先系统会创建一个进程.这个进程里面已经包含了一个线程.这个线程执行的代码就是,main方法

对于 run():

run方法并未启动新线程,该程序中还是只有主线程,而且是顺序执行,当run()上面执行完毕后,下面的打印才会被执行.
在这里插入图片描述

7.并发执行的优势

提高运行速度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值