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();
    }
}

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

相关推荐
songbaoxian7 分钟前
ElasticSearch
java·linux·elasticsearch
非 白21 分钟前
【Java】代理模式
java·开发语言·代理模式
Good Note32 分钟前
Golang的静态强类型、编译型、并发型
java·数据库·redis·后端·mysql·面试·golang
我就是我3521 小时前
记录一次SpringMVC的406错误
java·后端·springmvc
向哆哆1 小时前
Java应用程序的跨平台性能优化研究
java·开发语言·性能优化
ekkcole2 小时前
windows使用命令解压jar包,替换里面的文件。并重新打包成jar包,解决Failed to get nested archive for entry
java·windows·jar
handsomestWei2 小时前
java实现多图合成mp4和视频附件下载
java·开发语言·音视频·wutool·图片合成视频·视频附件下载
全栈若城2 小时前
03 Python字符串与基础操作详解
java·开发语言·python
伯牙碎琴3 小时前
二、Spring Framework基础:IoC(控制反转)和DI(依赖注入)
java·spring·log4j
菲力蒲LY3 小时前
输入搜索、分组展示选项、下拉选取,全局跳转页,el-select 实现 —— 后端数据处理代码,抛砖引玉展思路
java·前端·mybatis