JavaEE——多线程

接着上篇博客我们已经学习了进程的相关概念,了解到进程里面的相关重要属性,那么这篇博客呢我们要学习一个新的知识点------线程!

一、引入进程的目的

首先引入进程这个概念,是为了解决"并发编程"这样的问题。因为CPU 再往小了做,比较困难了,这是因为 CPU 进入了多核心的时代,要想进一步提高程序的执行速度,就需要充分的利用 CPU 的多核资源。

但是呢,多进程编程,已经可以解决并发编程的问题了已经可以利用起来 cpu 多核资源了。

弊端:

进程太重了!(消耗资源多 & 速度慢)。

1、创建一个进程,开销比较大。

2、销毁一个进程,开销也比较大。

3、调度一个进程,开销也比较大。

说进程重,主要就是重在**"资源分配/回收。**

线程:

所以我们的线程也就应运而生.线程也叫做"轻量级进程"。

目的就是解决并发编程问题的前提下,让创建,销毁,调度的速度,更快一些!
轻的原因:主要是"把申请资源/释放资源"的操作给省下了。

二、多线程的优点

假设小帅开了一家工厂,他在这个工厂里买了一台机器用来生产空调,小帅发现自己的产品销量不错,于是他想加大生产力度,那可以通过什么方法来提高生产效率呢?

方案一:在这个工厂旁边再开一家工厂购买一台机器用来生产空调(多进程的解决方案)

方案二 :在原本的工厂内再购买一台 机器用来生产空调**(多线程的解决方案)**

那么很显然第二种解决方案更划算,场地和空间都是复用之前的,共用一套资源。

1.线程和进程的关系:进程包含线程!

只有第一个线程启动的时候,开销是比较大的.后续线程就省事了个进程可以包含一个线程,也可以包含多个线程.(不能没有)。你线程1 new 的对象在线程2,3,4 里都可以直接使用。线程1 打开的文在线程2,3,4 里都可以直接使用。

同一个进程里的多个线程之间,共用了进程的同一份资源(主要指的是 内存 和 文件描述符表)。
注:操作系统,实际调度的时候是以线程为单位进行调度的.

如果每个进程有多个线程了,每个线程是独立在 CPU 上调度的。

**线程是操作系统调度执行的基本单位,**每个线程也都有自己的执行逻辑 (执行流)。

2.面试题:进程和线程之间的区别与联系。

1.进程包含线程! 一个进程里面可以有一个线程,也可以有多个线程。

2.进程线程都能解决并发编程问题场景.但是进程在频繁创建和销毁中,开销更高.线程开销更低,(线程比进程更轻量)。

3.进程是系统分配资源(内存,文件资源....) 的基本单位。线程是系统调度执行的基本单位(CPU)。

4进程之间是相互独立的各自有各自的虚拟地址空间,同一个进程内部的多个线程之间,共用同一个内存空间以及文件资源,一个进程挂了其他进程一般都没事。但是一个线程挂了,很可能把整个进程都带走!

三、第一个多线程程序

那么在Java中进行多线程编程的话如何实现呢?

在 Java 标准库中,就提供了一个 Thread 类,来表示/操作线程。
Thread 类也可以视为是 Java 标准库提供的 API。

即创建好的 Thread 实例,其实和操作系统中的线程是一一对应的关系。

操作系统,提供了一组关于线程的 API(C 语言风),Java 对于这组 API 进一步封装了一下,就成了 Thread 类。

(一)、Thread类的基本用法
写法一: 继承自Thread,重写run()

那么通过Thread类创建线程有很多种方法,其中最简单的就是创建子类继承自Thread,**重写run()**方法。

java 复制代码
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("hello Thread");
    }
}

注意,run()方法体中描述了这个线程内部要执行哪些代码。因为每个线程都是并发执行的.(各自执行各自的代码)因此就需要告知这个线程,你执行的代码是什么。

run()方法中的逻辑,是在新创建出来的线程中,被执行的代码,意思就是并不是我一定义这个类,一写 run 方法,线程就创建出来了,相当于领导已经把任务安排好了,我还没开始干呢!

java 复制代码
public class demo1 {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();
    }
}

需要调用这里的start()方法,才是真正在系统中创建了线程,才开始真正执行上面的run操作。

那么我们再写一个程序来仔细观察一下线程的执行情况,我们这里通过一个while循环来打印一个语句,通过sleep方法来控制打印的速度。

java 复制代码
class MyThread2 extends Thread{
    @Override
    public void run() {
        while (true){
            System.out.println("hello thread!");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class demo2 {
    public static void main(String[] args) {
        Thread t = new MyThread2();
        t.start();
 
        while (true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

那么我们知道,在一个进程中,至少会有一个线程。

在一个java 进程中,也是至少会有一个调用 main 方法的线程(这个线程不是你手动搞出来的)。

自己创建的 t 线程和 自动创建的 main 线程,就是并发执行的关系(宏观上看起来是同时执行)。

此处的"并发 = 并行+并发"宏观上是区分不了并行和并发的.都取决于系统内部的调度

我们可以发现现在两个线程,都是打印一条,就休眠个1s。

当 1s 时间到了之后,系统先唤醒谁呢?

通过运行结果看起来这个顺序不是完全确定**(随机的)**。

每一轮,1s 时间到了之后,到底是先唤醒 main 还是 thread,这是随机的,对于操作系统来说,内部对于线程之间的调度顺序,在宏观上可以认为是随机的,即所谓的------抢占式执行!

那么对于这个随机性,会给多线程编程带来很多其他的麻烦!!

写法二:

创建一个类,实现Runnable接口,再创建Runnable实例传给Thread实例,通过Runnable来描述任务的内容。

java 复制代码
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("hello java");
    }
}
public class demo3 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();
    }
}
写法三:匿名内部类

也就是上面两种写法的翻版,通过一个匿名内部类来实现:

java 复制代码
public class demo4 {
    public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run() {
                System.out.println("hello javaEE");
            }
        };
        t.start();
    }
}

* 通过匿名内部类来实现。

* 创建一个匿名内部类,继承自Thread类。

* 同时重写run方法。

* 同时再new出这个匿名内部类的实例。

写法四:匿名内部类的方法2
* new 的 Runnable,针对这个创建的匿名内部类 同时new出的Runnable实例传给Thread的构造方法。

* 通常认为这种写法好一点 能够做到让线程和线程执行的任务,更好的进行解耦。写代码追求高内聚,低耦合。
* Runnable 单纯的只是描述了一个任务 至于这个任务是要通过一个进程来执行,还是线程池来执行 Runnable本身并不关心。

java 复制代码
public class demo5 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 多线程");
            }
        });
        t.start();
    }
}
写法五:Lambda(可以代替内部类的方式)
java 复制代码
public class demo6 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
        System.out.println("hello Thread");
    });
        t.start();
    }
}

创建多线程的方式还有很多种,以上五种必须熟练掌握

相关推荐
m51274 分钟前
LinuxC语言
java·服务器·前端
IU宝9 分钟前
C/C++内存管理
java·c语言·c++
瓜牛_gn9 分钟前
依赖注入注解
java·后端·spring
hakesashou10 分钟前
Python中常用的函数介绍
java·网络·python
佚先森19 分钟前
2024ARM网络验证 支持一键云注入引流弹窗注册机 一键脱壳APP加固搭建程序源码及教程
java·html
古月居GYH33 分钟前
在C++上实现反射用法
java·开发语言·c++
儿时可乖了1 小时前
使用 Java 操作 SQLite 数据库
java·数据库·sqlite
ruleslol1 小时前
java基础概念37:正则表达式2-爬虫
java