Java 多线程1——认识多线程 + 编写第一个多线程程序


前言

本人是一个刚刚上路的IT新兵,菜鸟!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果这篇文章可以帮助到你,劳请大家点赞转发支持一下!

本篇文章将带大家了解java编程中的重点内容,线程,在java中与多进程编程相比,多线程编程对java来说更友好。

本篇文章也需要大家先对进程有一定的了解,废话不多,开车了。
贴心链接:🥰🥰🥰进程篇


一、认识线程

进程篇,已经带大家了解过进程了。

🙌🙌进程就是一个跑起来的程序,操作系统会为这个进程创造一个PCB类型的结点,并把这个结点添加到 组织进程的数据结构中去。

🥳🥳而进程可以通过并行与并发,从而实现多个进程同时运行的效果。

😎😎😎线程可以看作是更加轻量级的进程。
进程可以看作是工厂,
线程可以看作是工厂里的流水线,

线程是更轻量级的进程,那么这一点体现在了哪里呢??🤔🤔🤔


重量级的进程

🤠🤠🤠进程的一个重要概念: 进程是操作系统资源分配的基本单位。


进程的创建,调度,与销毁的成本其实都挺高的。
拿进程的创建来举例。

创建一个进程之所以成本高的主要是因为资源分配
资源分配往往都是比较耗时的操作。

进程需要系统为自己分配一块内存。

系统去分配这一块内存,就需要去遍历自己的空闲内存的表(是一种数据结构),找到一块大小匹配的空间,然后再进行分配。

😐😐😐所以此时相对来说,进程就是重量级的。

所以频繁的创建,调度,与销毁进程,成本是相当之高的。

因此,聪明的👨‍💻程序猿就引入了一个新的概念,线程。


轻量级的线程

🤩🤩🤩引入线程的意义:
1️⃣单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU 资源(即并发编程)。

2️⃣ 有些任务场景需要 “等待 IO”, 为了让等待 IO 的时间能够去做一些其他的工作, 也需要用到并发编程。

3️⃣ 创建线程比创建进程更快。

4️⃣ 销毁线程比销毁进程更快。

5️⃣ 调度线程比调度进程更快。

🤠🤠🤠线程的一个重要概念: 线程是操作系统调度执行的基本单位。
进程篇提到的调度,本质上是去调度线程


进程之所以重量级就在于其资源分配。
所以线程就要规避掉这个缺点。

线程的特点
1️⃣线程被包含在进程中。(一个进程可以包含一个或多个线程)
2️⃣一个线程就是一个 “执行流”, 每个线程之间都可以按照顺序执行自己的代码.,多个线程之间 “同时” 执行着多份代码。
3️⃣进程里的所有线程,共用系统分配给进程的这一份资源。

所以进程启动,创建第一个线程时,需要耗费成本去向系统申请资源,在这个进程中创建其他的线程时,就不需要在向系统申请资源,从而减少创建成本与资源开销。
所以进程的创建与销毁的效率就提高了许多。


总结

进程就好像一个工厂,
线程就好像工厂里的流水线,

新建一个工厂与新建一个流水线,一定是新建工厂开销更大。

可以通过新建线程,来共同完成任务。


但也不是线程越多越好。

(直接化身大老板🤑🤑🤑) 假设有一家餐馆,一共可以供20桌客人吃饭。

(过度压榨线程,效率低下😶‍🌫️😶‍🌫️😶‍🌫️) 如果只有1个服务员(线程),可能会效率不够,忙不过来,此时就需要更多线程帮忙。

(没有充分发挥线程的作用,性能过剩😐😐😐)如果有20个服务员(线程),那么就变成一对一服务了(且这还是客人坐满的情况下),显然是成本高了,效率也没高多少。

(不把资源当回事,就是壕🤩🤩🤩) 如果有40个服务员(线程),那么服务员就会抢活干,抢不到活的服务员就只能闲着,这不就是白发工资吗 (资源浪费)。

而且如果一个线程崩了,会影响到甚至让其他线程也崩掉,进程没准也会挂。

  • 20卓客人,要四五个服务员就够了,所以线程也如此,在数量在于合理,不在多。

进程与线程的区别:
1️⃣ 进程包含线程(进程中至少包含一个线程,即主线程),线程在进程里。

2️⃣ 每个进程都有自己独立的内存空间与文件描述符表,同一个进程中的多个线程,共享该进程的内存空间与文件描述符表。

3️⃣ 进程是操作系统资源分配的基本单位,线程是操作系统调度执行的基本单位。

4️⃣ 进程之间具有独立性,一个进程出现bug不会影响到其他进程。
线程之间具有共享性,一个线程出现bug可能会影响到其他线程,导致这一个进程都被带走。

二、编写多线程程序

接下来会深刻的感受到以下两点

1️⃣每个线程都是一个独立的执行流

2️⃣多个线程之间是 “并发” 执行的


java标准库中提供了一个Thread类,来表示一个线程。

  • run方法,通过重写Thread类中的run方法,来对线程的执行代码进行编写。
    run可以称为是线程的入口方法

  • start方法,执行这个方法,才是真正的在系统里创建了一个新线程,start方法会自动调用run方法。

继承 Thread 类

使用Thread类中的run方法来描述线程入口

class MyThread extends Thread {

	//重写run方法,编写线程代码
    @Override
    public void run() {
        while (true) {
            System.out.println("hello thread");
        }
    }
}
public class threadDemo1 {
    public static void main(String[] args) {
    	//创建一个线程
        Thread mythread = new MyThread();
		//启动线程
        mythread.start();
        while (true) {
            System.out.println("hello main");
        }
    }
}

运行这个程序,此时,idea进程会创建一个java进程

此时java进程中有两个个线程 main线程(主线程)与thread线程(未执行)。
直到主线程执行到了mythread.start();这个语句,才真的在操作系统的底层创建出一个线程,并且开始执行这个新线程
这两个线程通过并行+并发方式,可能是并行执行,也可能是并发执行,也可能两者一会并行,一会并发。(多个线程在CPU上调度执行的顺序是不确定的,随机的)

不管是并行执行还是并发执行,两个线程都是在同一个控制台输出,所以只能交替输出。


也可以通过匿名内部类来实现创建 Thread子类对象

public class MyThread {
    public static void main(String[] args) {
    	//通过匿名内部类来编程线程代码
        Thread thread = new Thread() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                }
            }
        };
        //启动线程
        thread.start();
        while (true) {
            System.out.println("hello main");
        }
    }
}

实现 Runnable 接口

使用Runnable interface来描述线程入口。

run方法是Runnable接口中的抽象方法。

class MyRunnable implements Runnable {
	//编写线程代码
    @Override
    public void run() {
        while (true) {
            System.out.println("hello start");
        }
    }
}
public class MyThread {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        //创建 Thread 类实例, 调用 Thread 的构造方法时将 MyRunnable 对象作为 target 参数.
        Thread thread = new Thread(myRunnable);
        //启动线程
        thread.start();
        while (true) {
            System.out.println("hello main");
        }
    }
}

也可以通过匿名内部类来创建 Runnable 子类对象

public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                }
            }
        });
        thread.start();
        while (true) {
            System.out.println("hello main");
        }
    }

也可以通过Lambda表达式创建 Runnable 子类对象

    public static void main(String[] args) {
        //通过Lambda表达式
        Thread thread = new Thread(()->{
            while (true) {
                System.out.println("hello thread");
            }
        });
        thread.start();
        while (true) {
            System.out.println("hello main");
        }
    }
}

总结

以上就是今天要讲的内容,本篇文章先让大家认识线程,还会有后续文章来继续带大家了解线程这一关键先生的。

路漫漫,不止修身也养性。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值