【Java开发】JUC基础 01:进程、线程、多线程

news2024/9/26 5:16:31

1 进程与线程

1.1 进程

开发写的代码称为程序,那么我们将程序运行起来,我们称之为进程

进程就是申请一块内存空间,将数据放到内存空间中去,是系统进行资源分配和调度的基本单位

📌 程序与进程的区别

  • 程序是数据和指令的集合,是一个静态的概念,就是一堆代码,可以长时间的保存在系统中;

  • 进程是程序运行的过程,是一个动态的概念,进程存在着生命周期,也就是说进程会随着程序的终止而销毁,不会永久存在系统中。

📢 进程间的交互通过 TCP/IP 端口实现

1.2 进程池

类似于线程池,进程池是资源进程,管理进程组成的应用及技术;不多介绍了,它主要用于处理成千上万的进程任务。

📌 概念

  • 资源进程:预先创建好空的进程,管理进程会把任务分发到空闲进程来处理;

  • 管理进程:负责创建资源进程,把工作交给空闲资源处理,回收已经处理完的资源进程;

1.3 线程

一个进程至少有一个线程(比如java程序至少有main线程和GC线程),线程是进程中的实际运作单位,操作系统能够进行运算调度的最小单位(CPU分配的最小单位)。

一个线程是进程中一个单一顺序的控制流,一个进程可以并发多个线程,每条线程并行执行不同的任务;

一个线程是进程的一条流水线,只用来执行程序,而不涉及到申请资源;

📌 进程和线程的关系

  • 比如说我们用手机打开微信,运行的微信就是开启的一个线程,当我们使用微信的一些功能,扫一扫,付款等,这些都是线程;

  • 进程包含线程,线程属于进程的子集;

📢 多个线程共享同一内存,通过共享的内存空间来交互

1.4 多线程

  • 并发:就是同一时刻,只有一个执行,但是同一个时间段内,两个线程都执行了。并发的实现依赖于CPU切换线程,因为切换的时间特别短,所以基本对于用户来说是无感知的;

  • 并行:是同一时刻,两个线程都在执行,这就要求两个CPU(或多核cpu)分别去执行两个线程;

如下图,分别是并发和并行状,多线程就是实现并发的一个手段

  • 串行:一个程序处理完当前进程,接着处理下一个进程,一个一个连着进行。

2 线程创建的三种方式

如图,线程创建主要有以下三种方式👇,第二种是重点。

2.1 继承 Thread 类

public class MyThread extends Thread{

    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("继承Thread实现了第"+i+"次调用;");
        }
    }

    /**
     * 继承thread 实现多线程  
     * 1. 类继承Thread  
     * 2. 重写run()  
     * 3. start() 调用  
     */
    //main线程--主线程
    public static void main(String[] args) {
        //创建一个线程对象
        MyThread myThread = new MyThread();
        //调用start()方法开启线程
        myThread.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("main调用了"+i+"次;");
        }
    }
}

如果电脑cpu非顶级,那么控制台输出如下:

可以看到它是无序的,这是因为MyThread线程和主线程在交替进行,也就是所谓的并发!

如果将myThread.start()替换为myThread.run(),那么只存在一个主线程,就会先执行run()方法,因此将先输出run()方法中的内容,再输出主线程中的内容,不妨试一下。

2.2 实现 Runable 接口

📢 一般建议使用该方法,因为可以避免Java单继承的局限性。

public class MyRunnable implements Runnable{

    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("实现Runnable实现了第"+i+"调用;");
        }
    }

    /**
     * 实现Runnable接口实现多线程
     * 1. 类实现Runnable
     * 2. 重写run()
     * 3. 将实现类放入Thread并调用start() 
     */
    public static void main(String[] args) {
        //创建runable接口的实现类对象
        MyRunnable myRunnable = new MyRunnable();
        //创建线程对象,通过线程对象来开启线程,代理,将2行代码简写为1行
//        Thread thread = new Thread(myRunnable);
//        thread.start();
        new Thread(myRunnable).start();

        for (int i = 0; i < 20; i++) {
            System.out.println("main调用了"+i+"次;");
        }
    }
}

执行main方法,控制台输出如下:

依旧是多线程的无序性。

2.3 实现 Callable 接口

📌主要有以下步骤:

  1. 实现 Callable 接口, 需要返回值

  1. 重写 call 方法,需要抛出异常

  1. 创建目标对象

  1. 创建执行服务:ExecutorService service = Executors.newCachedThreadPool()

  1. 提交执行:提交执行:Future<> submit = service. Submit (对象);

  1. 获取结果 :Boolean aBoolean = submit. Get ();

  1. 关闭服务:service. ShutdownNow ();

public class MyCallable implements Callable<Boolean> {

    @Override
    public Boolean call() {
        return false;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable();
        ExecutorService service = Executors.newCachedThreadPool();
        Future<Boolean> submit = service.submit(myCallable);
        System.out.println(submit.get());
        service.shutdown();
    }
}

3 多线程实践举例

3.1 下载网图(继承 Thread 类)

📌 引入jar包

因为我创建的是maven项目,所以直接引入io依赖就行了。

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.11.0</version>
        </dependency>

📌 编写代码

import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;

//练习thread实现多线程下载图片
public class ImageThread extends Thread{

    private String url;

    private String name;

    public ImageThread(String url,String name){
        this.url = url;//图片地址
        this.name = name;//图片地址
    }

    @Override
    public void run() {
        WebDownLoader webDownLoader = new WebDownLoader();
        webDownLoader.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
    }

    public static void main(String[] args) {
        ImageThread t1 = new ImageThread("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","1.jpg");
        ImageThread t2 = new ImageThread("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","2.jpg");
        ImageThread t3 = new ImageThread("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","3.jpg");
        t1.start();
        t2.start();
        t3.start();
    }
}

//下载器
class WebDownLoader{
    //下载方法
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

执行main方法,输出如下:

说明这三个线程并无严格顺序,是交替进行的。

3.2 下载网图(实现 Runable 接口)

📌 编写代码

import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;

//练习thread实现多线程下载图片
public class ImageThread implements Runnable{

    private String url;

    private String name;

    public ImageThread(String url,String name){
        this.url = url;//图片地址
        this.name = name;//图片地址
    }

    @Override
    public void run() {
        WebDownLoader webDownLoader = new WebDownLoader();
        webDownLoader.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
    }

    public static void main(String[] args) {
        ImageThread t1 = new ImageThread("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","1.jpg");
        ImageThread t2 = new ImageThread("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","2.jpg");
        ImageThread t3 = new ImageThread("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","3.jpg");
        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();
    }
}

//下载器
class WebDownLoader{
    //下载方法
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.3 模拟抢票(实现 Runable 接口)

📌 编写代码

//多个线程同时操作同一个对象(买火车票)
public class TicketThread implements Runnable {

    //票数
    private int tickerNums = 10;

    @Override
    public void run() {
        while (true){
            if (tickerNums <= 0){
                break;
            }
            //模拟延时
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+tickerNums--+"张票");
        }
    }

    public static void main(String[] args) {
        TicketThread ticketThread = new TicketThread();
        new Thread(ticketThread,"小明").start();//第二个参数是线程命名
        new Thread(ticketThread,"小红").start();
        new Thread(ticketThread,"小王").start();
    }
}

控制台输出如下:

很明显能发现个问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱!

这会在之后文章进行解决~

3.4 模拟龟兔赛跑(实现 Runable 接口)

📌 编写代码

public class Race implements Runnable{

    //胜利者--只有一个
    private static String winner;

    @Override
    public void run() {
        for (int i = 1; i <= 15; i++) {
            //模拟兔子休息,每5步进行休息
            if (Thread.currentThread().getName().equals("兔子")&&i%5==0){
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //判断比赛是否完成
            boolean flag = gameover(i);
            //若比赛结束,停止循环
            if (flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
        }
    }

    //判断比赛是否完成
    public boolean gameover(int steps){
        if (winner != null){ //不写该限制,会出现两个winer
            return true;
        }else {
            if (steps == 15){
                winner = Thread.currentThread().getName();
                System.out.println("winner is"+winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();

    }

控制台输出如下👇,乌龟成为胜利者,主要是模拟了两个线程进行循环输出。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/376234.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QML Item

在QML中所有的可视项目都继承自Item&#xff0c;虽然Item本身没有可视化的外观&#xff0c;但它定义了可视化项目的所有属性。 Item可以作为容器使用&#xff1a; Item{Rectangle{id:retc}Rectangle{id:retc1}Rectangle{id:retc2}Rectangle{id:retc3}} item拥有children属性…

MyBatis学习笔记(七) —— 特殊SQL的执行

7、特殊SQL的执行 7.1、模糊查询 模糊查询的三种方式&#xff1a; 方式1&#xff1a;select * from t_user where username like ‘%${mohu}%’ 方式2&#xff1a;select * from t_user where username like concat(‘%’,#{mohu},‘%’) 方式3&#xff1a;select * from t_u…

DolphinScheduler跨版本升级1.3.8至3.0.1

DolphinScheduler跨版本升级1.3.8至3.0.1Refer背景基础环境依赖版本升级修改pom.xml问题解决MYSQL升级1.文件替换2.修改表结构t_ds_process_definitiont_ds_alertt_ds_process_instance3.时间参数修改4.数据库升级DOLPHIN安装zookeeper集群创建用户dolphinscheduler_env.shinst…

指针变量作为函数参数详解,形参和实参之间的值传递如何传递?如何改变指针变量所指向的变量?

函数的参数不仅可以是整型&#xff0c;浮点型&#xff0c;字符型等数据&#xff0c;还可以是指针类型&#xff1b;它的作用是将一个变量的地址传送到另一个函数中。 关于地址&#xff0c;指针&#xff0c;指针变量可以参考我的上一篇文章&#xff1a; 地址&#xff0c;指针&…

线程的基本方法

线程等待&#xff1a;wait方法 调用wait方法的线程会进入WAITING状态&#xff0c;只有等到其他线程的通知或程序被中断才会返回。调用wait方法后会释放对象的锁&#xff0c;因此 wait方法一般被用于同步方法或同步代码块中 。 线程睡眠&#xff1a;sleep方法 调用sleep方法会导…

Spring Boot 版本升级2.2.11.RELEASE至2.7.4

2.2.11.RELEASE > 2.7.4项目更新spring-boot-starter-parent 主依赖&#xff0c;导致项目跑不起了日志也没有输出有用信息&#xff0c;自己查看源码调试启动入口打断点&#xff0c;一步步进入方法定位项目停止代码我的项目执行到SpringApplication.class 的152行代码会停止项…

华为HCIE学习之Openstack Glance组件(glance对接swift)

文章目录一、Glance的结构二、服务部署流程三、将glance存储在swift中1、默认使用swift来存储2、指定可以存在swift中3、swift版本4、keystone的endpoint地址&#xff08;当glance去找swift时通过keystone去找&#xff09;5、租户名:用户名&#xff0c;用户必须拥有admin角色6、…

【C语言】自定义类型:结构体、枚举、联合

目录 1.结构体 1.1结构体类型 1.2结构体的自引用 1.3结构体的初始化 1.4结构体内存对齐 //对齐 //offsetof //修改默认对齐数 1.5结构体传参 2.位段 2.1位段的内存开辟 2.2位段的内存分配 3.枚举 4.联合&#xff08;共用体&#xff09; //判断大小端 1.结构体…

【GO】k8s 管理系统项目23[前端部分–工作负载-Pod]

k8s 管理系统项目[前端部分–工作负载-Deployment] 1. 代码部分 1.1 准备工作 由于Pod页面和Deployment内容差不多.那么就直接把Deployment的内容复制过来.再做修改. 替换Deployment为Pod替换Deploy为Pod替换deployment为pod替换deploy为pod禁用新增的按钮,删除新增方法,表…

django后端服务、logstash和flink接入VictoriaMetrics指标监控

0.简介 通过指标监控可以设置对应的告警&#xff0c;快速发现问题&#xff0c;并通过相应的指标定位问题。 背景&#xff1a;使用的 VictoriaMetrics(简称 VM) 作为监控的解决方案&#xff0c;需要将 django 服务、logstash 和 flink 引擎接入进来&#xff0c;VM 可以实时的获…

SpringBoot:SpringBoot配置文件.properties、.yml 和 .ymal(2)

SpringBoot配置文件1. 配置文件格式1.1 application.properties配置文件1.2 application.yml配置文件1.3 application.yaml配置文件1.4 三种配置文件优先级和区别2. yaml格式2.1 语法规则2.2 yaml书写2.2.1 字面量&#xff1a;单个的、不可拆分的值2.2.2 数组&#xff1a;一组按…

操作系统权限提升(十八)之Linux提权-内核提权

Linux 内核提权 Linux 内核提权原理 内核提权是利用Linux内核的漏洞进行提权的&#xff0c;内核漏洞进行提权一般包括三个环节&#xff1a; 1、对目标系统进行信息收集&#xff0c;获取到系统内核信息及版本信息&#xff1b; 2、根据内核版本获取其对应的漏洞以及EXP 3、使…

第七届蓝桥杯省赛 C++ A/B组 - 四平方和

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;蓝桥杯题解集合 &#x1f4dd;原题地址&#xff1a;四平方和 &#x1f4e3;专栏定位&#xff1a;为想参加蓝桥杯的小伙伴整理常考算法题解&#xff0c;祝大家…

Docker简介与用法

文章目录1、Docker简介1.1、Docker能解决什么问题1.2、什么是虚拟机技术1.2.1、虚拟机的缺点1.3、什么是容器1.3.1、容器与虚拟机比较1.4、分析 Docker 容器架构1.4.1、Docker客户端和服务器1.4.2、Docker 镜像(Image)1.4.3、Docker 容器(Container)1.4.4、Docker 仓库(reposit…

Windows程序员学习Linux环境内存管理

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天我们来重新审视一下Windows程序员如何学习Linux环境内存管理。由于很多程序在Windows环境下开发好后&#xff0c;还要部署到Linux服务器上去&#xff0c;所以作为Windows程序员有必要学习Linux环境的内存…

【计算机三级网络技术】 第三篇 IP地址规划技术

IP地址规划技术 文章目录IP地址规划技术一、IP 地址规划以及划分地址新技术1.IP地址的标准分类&#xff08;第一阶段&#xff09;2.划分子网的三级地址结构(第二阶段)3.构成超网的无类域间路由技术(第三阶段)4.网络地址转换技术(第四阶段)二、IP 地址分类1.A类、B类与C类IP地址…

数据的表示和运算

文章目录数制与编码进制间的转换BCD码定点数与浮点数定点数是什么&#xff1f;浮点数是什么&#xff1f;定点数与浮点数的区别机器数和真值原码、反码、补码、移码基本定义整数的加减法刷题小结最后数制与编码 进制间的转换 二进制、八进制、十进制、十六进制之间的转换不用多…

前端杂学1

1.简单且必须掌握的 1.MVVM是什么 将MVC中的V变为了MVVM&#xff0c;实现了双向绑定。其中VM就是vue的作用&#xff0c;这样页面的动态化可以通过vue来操作&#xff0c;而不是页面直接与后端操作&#xff0c;实现了前后端的分离 2.为什么vue采用异步渲染 &#xff1f; 调…

Kubernetes之服务发现

本文使用wordpressmysql搭建个人博客来讲解服务发现相关知识。 环境准备 wordpress需要连接到mysql才能正常工作&#xff0c;所以需要为mysql的pod创建一个mysql的svc&#xff0c;只要不删除重建svc&#xff0c;其IP不会变。 此时wordpress的pod需要连接mysql的svc的时候&…

HyperGBM用4记组合拳提升AutoML模型泛化能力

本文作者&#xff1a;杨健&#xff0c;九章云极 DataCanvas 主任架构师 如何有效提高模型的泛化能力&#xff0c;始终是机器学习领域的重要课题。经过大量的实践证明比较有效的方式包括&#xff1a; 利用Early Stopping防止过拟合通过正则化降低模型的复杂度使用更多的训练数…