Java基础——多线程

1. 线程

  1. 是一个程序内部的一条执行流程
  2. 程序中如果只有一条执行流程,那这个程序就是单线程的程序

2. 多线程

  1. 指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行)

2.1. 如何创建多条线程

  1. Java通过java.lang.Thread类的对象来代表线程
2.1.1. 方式一:继承Thread类
// 1. 继承Thread类
public class MyThread extends Thread{
    // 2. 重写run方法
    @Override
    public void run() {
        System.out.println("子线程");
    }
}
public static void main(String[] args) {
    // 3. 创建子线程对象
    Thread t = new MyThread();

    // 4. 启动子线程
    t.start();

    System.out.println("主线程");
}
  1. 优点:编码简单
  2. 缺点:已经继承Thread,无法继承其他类,不利于功能的扩展
  3. 启动线程必须调用start方法,而不能调用run方法,否则会将线程对象当作一个普通的对象。
  4. 不要把主线程任务放在启动子线程之前。
2.1.2. 方式二:实现Runnable接口
// 1. 定义任务类, 实现Runnable接口
public class MyRunnable implements Runnable {
    // 2. 重写run方法
    @Override
    public void run() {
        System.out.println("Runnable子线程");
    }
}
public static void main(String[] args) {
    // 3. 创建一个任务对象
    Runnable runnable = new MyRunnable();

    // 4. 把任务对象交给一个线程对象, 启动线程
    new Thread(runnable).start();

    System.out.println("主线程");
}
  1. 任务类只是实现接口,可以继续继承其他类,实现其他接口,扩展性强
2.1.3. 方式三:利用Callable接口、FutureTask类实现
  1. 以上两种方式的问题:如果线程执行完毕后有一些数据需要返回,他们重写的run方法均不能直接返回结果。
  2. 使用Callable接口,可以返回线程执行完毕后的结果

  1. 未来任务对象的作用,是一个任务对象,实现了Runnable接口;可以在线程执行完毕之后,用未来任务对象调用get方法获取执行完的返回结果
// 1. 实现Callable接口
public class MyCallable implements Callable<String> {
    // 2. 重写call方法
    @Override
    public String call() throws Exception {
        // 描述线程任务, 返回线程执行后的结果
        int sum = 0;
        for(int i = 1; i <= 100; i++){
            sum += i;
        }
        return String.valueOf(sum);
    }
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
    // 3. 创建一个Callable对象
    Callable<String> callable = new MyCallable();

    // 4. 将Callable对象封装成FutureTask任务对象
    FutureTask<String> futureTask = new FutureTask<>(callable);

    // 5. 使用任务对象创建一个线程对象, 并启动
    new Thread(futureTask).start();

    // 6. 获取线程执行完毕返回的结果
    String s = futureTask.get();
    System.out.println(s);
}

2.2. Thread的常用方法

3. 线程安全

  1. 多个线程,同时访问同一共享资源,且存在修改该资源的时候,可能会出现业务安全问题

4. 线程同步

  1. 是用于解决线程安全问题的方案
  2. 让多个线程实现先后依次访问共享资源,这样就解决了安全问题

4.1. 线程同步的方案

  1. 加锁,每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程再加锁进来。

4.2. 加锁的方式

4.2.1. 方式一:同步代码块
  1. 作用:把访问共享资源的核心代码块给上锁,以此保证线程安全
  2. 写法

  1. 原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行
  2. 注意事项:对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象)
  3. Ctrl + ALT + T选第九个,快速生成同步代码块
  4. 建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象;静态方法建议使用字节码(类名.class)对象作为锁对象
4.2.2. 方式二:同步方法
  1. 作用:把访问共享资源的核心方法给上锁,以此保证线程安全。

  1. 原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行
4.2.3. 方式三:Lock锁
  1. 可以通过它创建锁对象,进行加锁和解锁
  2. Lock是接口,不能直接实例化,可以采用他的实现类ReentrantLock来创建锁对象。

5. 线程通信

  1. 当多个线程共同操作共享资源时,线程间通过某种方式互相告知自己的状态,一相互协调,并避免无效的资源争夺
  2. 生产者消费者问题

上述方法应该使用当前同步锁对象进行调用

public class Test5 {
    public static void main(String[] args) {
        // 有三个生产者线程负责生产包子, 每次只能有一个包子放在桌子上
        // 两个消费者线程负责吃包子, 每次只有一个线程能将包子吃掉
        // 1. 创建一个桌子对象
        Desk desk = new Desk();

        // 2. 创建三个厨师线程
        new Thread(() ->{
            while (true) {
                desk.put(); // 抢桌子
            }
        }, "厨师1").start();

        new Thread(() ->{
            while (true) {
                desk.put(); // 抢桌子
            }
        }, "厨师2").start();

        new Thread(() ->{
            while (true) {
                desk.put(); // 抢桌子
            }
        }, "厨师3").start();

        // 3. 创建两个吃货线程
        new Thread(() -> {
            while (true){
                desk.get();
            }
        },"吃货1").start();

        new Thread(() -> {
            while (true){
                desk.get();
            }
        },"吃货2").start();
    }
}
public class Desk {
    private List<String> list = new ArrayList<>();

    public synchronized void put() {
        try {
            String name = Thread.currentThread().getName();
            if(list.size() == 0){
                list.add(name + "包的包子");
                System.out.println(name + "包了一个包子");
                Thread.sleep(2000);

                // 唤醒别人, 等待自己
                this.notifyAll();
                this.wait();
            }else {  // 有包子了
                // 唤醒别人, 等待自己
                this.notifyAll();
                this.wait();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public synchronized void get() {

        try {
            if(list.size() == 1){ // 有包子
                String name = Thread.currentThread().getName();
                System.out.println(name + "吃了" + list.get(0));
                list.clear(); // 清空
                Thread.sleep(1000);

                // 唤醒别人, 等待自己
                this.notifyAll();
                this.wait();
            } else {
                // 唤醒别人, 等待自己
                this.notifyAll();
                this.wait();
            }
        }
        catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

6. 线程池

  1. 线程池就是一个可以复用线程的技术
  2. 代表线程池的接口,ExecutorService

6.1. 创建线程池对象

6.1.1. 使用ExecutorService的实现类ThreadPoolExecutor创建线程池对象

// 通过ThreadPoolExecutor创建一个线程池对象
/*
public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue,
                      ThreadFactory threadFactory,
                      RejectedExecutionHandler handler)
 */
ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
        TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());

注意事项:

  1. 新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程
  2. 核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务
  3. 如果是计算密集型任务,核心线程数 = CPU核数 + 1
  4. 如果是IO密集型任务,核心线程数 = CPU核数 * 2
6.1.2. 使用Executors(线程池工具类)调用静态方法返回不同特点的线程池对象

  1. 这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象
  2. 大型并发系统环境中使用Executors如果不注意可能会出现系统风险

6.2. 线程池处理Runnable任务、Callable任务

7. 并发、并行、线程的生命周期

7.1. 进程

  1. 正在运行的程序(软件)就是一个独立的进程。
  2. 线程是属于进程的,一个进程中可以同时运行多个线程。
  3. 进程中的多个线程其实是并发和并行执行的

7.2. 并发

进程中的线程是由CPU负责调度执行的,但CPU能同时处理的线程数量有限,为了保证全部线程都能往前执行,CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发。

7.3. 并行

在同一时刻上,同时有多个线程在被CPU调度执行。

7.4. 线程的生命周期

线程从创建到销毁的过程中,经历的各种状态及状态的转换

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HarmonyOS ArkUI(基于ArkTS) 常用组件

一 Button 按钮 Button是按钮组件&#xff0c;通常用于响应用户的点击操作,可以加子组件 Button(我是button)Button(){Text(我是button)}type 按钮类型 Button有三种可选类型&#xff0c;分别为胶囊类型&#xff08;Capsule&#xff09;、圆形按钮&#xff08;Circle&#xf…

【FPGA开发】AXI-Stream总线协议解读

文章目录 AXI-Stream概述协议中一些定义字节定义流的定义 数据流类别字节流连续对齐流连续不对齐流稀疏流 协议的信号信号列表 文章为个人理解整理&#xff0c;如有错误&#xff0c;欢迎指正&#xff01; 参考文献 ARM官方手册 《IHI0051B》 AXI-Stream概述 协议中一些定义 A…

谷粒商城のMySQL集群分库分表

文章目录 前言一、MySQL的集群架构二、MySQL主从同步实践1.创建主节点实例2.创建从节点实例3.修改配置4.开始同步4.测试主从同步效果5.小结 三、MySQL分库分表1.配置sharding-proxy2.测试sharding-proxy3.小结 前言 本篇是谷粒商城集群部署篇&#xff0c;搭建MySQL集群以及分库…

计算机组成原理对于学习嵌入式开发的意义

计算机组成原理对于学习嵌入式开发的意义 前言 最近有位同学向我咨询&#xff0c;问学习嵌入式开发需不需要学习硬件&#xff1f;进而引申到了需不需要学习计算机组成原理呢&#xff1f; 正文 首先计算机组成原理是计算机科学与技术专业的一门核心基础课程&#xff0c;它深入…

npm list -g --depth=0(用来列出全局安装的所有 npm 软件包而不显示它们的依赖项)

您提供的命令 npm list -g --depth0 是在 Node Package Manager (npm) 的上下文中使用的&#xff0c;用来列出全局安装的所有 npm 软件包而不显示它们的依赖项。 这是它的运作方式&#xff1a; npm list -g --depth0-g: 指定列表应包括全局安装的软件包。--depth0: 限制树形结…

SpringBoot 2.2.10 无法执行Test单元测试

很早之前的项目今天clone现在&#xff0c;想执行一个业务订单的检查&#xff0c;该检查的代码放在test单元测试中&#xff0c;启动也是好好的&#xff0c;当点击对应的方法执行Test的时候就报错 tip&#xff1a;已添加spring-boot-test-starter 所以本身就引入了junit5的库 No…

多表查询综合归纳

目录 1. 多表关系 1.1 一对多&#xff08;多对一&#xff09; 1.2 多对多 1.3 一对一 2. 多表查询概述 2.1 熟悉表 2.2 笛卡尔积 2.3 消除笛卡尔积 2.4 多表查询分类 3. 内连接 3.1 隐式内连接 3.2 显式内连接 4. 外连接 4.1 左外连接 4.2 右外连接 5. 自连接 …

python爬虫(二)爬取国家博物馆的信息

import requests from bs4 import BeautifulSoup# 起始网址 url https://www.chnmuseum.cn/zx/xingnew/index_1.shtml # 用于存储所有数据 all_data [] page 1 global_index 1 # 定义全局序号变量并初始化为1 while True:html_url requests.get(url).textif requests.get…

基于NI Vision和MATLAB的图像颜色识别与透视变换

1. 任务概述 利用LabVIEW的NI Vision模块读取图片&#xff0c;对图像中具有特征颜色的部分进行识别&#xff0c;并对识别的颜色区域进行标记。接着&#xff0c;通过图像处理算法检测图像的四个顶点&#xff08;左上、左下、右上、右下&#xff09;&#xff0c;并识别每个顶点周…

Qt_day7_文件IO

目录 文件IO 1. QFileDialog 文件对话框&#xff08;熟悉&#xff09; 2. QFileInfo 文件信息类&#xff08;熟悉&#xff09; 3. QFile 文件读写类&#xff08;掌握&#xff09; 4. UI操作与耗时操作&#xff08;掌握&#xff09; 5. 多线程&#xff08;掌握&#xff09;…

[论文笔记]An LLM Compiler for Parallel Function Calling

引言 今天带来一篇优化函数调用的论文笔记——An LLM Compiler for Parallel Function Calling。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 当前的函数(工具)调用方法通常需要对每个函数进行顺序推理和操作&…

网络性能测试

一、iperf网络性能测试工具 测试udp丢包率 在服务器启动 iperf 服务端 iperf -p 9000 -s -u -i 1参数说明&#xff1a; -p : 端口号 -s : 表示服务端 -u : 表示 udp 协议 -i : 检测的时间间隔(单位&#xff0c;秒) 在客户端&#xff0c;启动 iperf 客户端 iperf -c xxx.xxx.14…

Rust语言在系统编程中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 Rust语言在系统编程中的应用 Rust语言在系统编程中的应用 Rust语言在系统编程中的应用 引言 Rust 概述 定义与原理 发展历程 Ru…

1-Equity-Transformer:求解NP-Hard Min-Max路由问题的顺序生成算法(AAAI-24)(完)(code)

文章目录 AbstractIntroduction问题表述Methodology多智能体位置编码公平上下文编码训练方案ExperimentsmTSP的性能评估mPDP的性能评估Related WorkConclusionAbstract 最小最大路由问题旨在通过智能体合作完成任务来最小化多个智能体中最长行程的长度。这些问题包括对现实世界…

数字后端教程之Innovus report_property和get_property使用方法及应用案例

数字IC后端实现Innovus中使用report_property可以报告出各种各样object的属性&#xff0c;主要有cell&#xff0c;net&#xff0c;PG Net&#xff0c;Pin&#xff0c;时钟clock&#xff0c;时序库lib属性&#xff0c;Design属性&#xff0c;timing path&#xff0c;timin arc等…

【Vitepress报错】Error: [vitepress] 8 dead link(s) found.

原因 VitePress 在编译时&#xff0c;发现 死链接(dead links) 会构建失败&#xff01;具体在哪我也找不到… 解决方案 如图第一行蓝色提示信息&#xff0c;设置 Vitepress 属性 ignoredeadlinks 为 true 可忽略报错。 .vuepress/config.js export default defineConfig(…

【开源风云】从若依系列脚手架汲取编程之道(七)

&#x1f4d5;开源风云系列 本篇文字量巨大&#xff0c;甚至在发表编辑之时造成编辑器卡顿&#xff0c;哈哈&#xff0c;最近在忙人生的另一项规划&#xff0c;文章更新就逐渐缓慢了&#xff0c;希望我们都逐渐走向自己的道路呀&#xff01; &#x1f34a;本系列将从开源名将若…

Docker+Django项目部署-从Linux+Windows实战

一、概述 1. 什么是Docker Docker 是一个开源的应用容器引擎&#xff0c;支持在win、mac、Linux系统上进行安装。可以帮助我们在一台电脑上创建出多个隔离的环境&#xff0c;比传统的虚拟机极大的节省资源 。 为什么要创建隔离的环境&#xff1f; 假设你先在有一个centos7.…

[项目代码] YOLOv5 铁路工人安全帽安全背心识别 [目标检测]

YOLOv5是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv5具有更高的…

蓝桥杯备赛(持续更新)

16届蓝桥杯算法类知识图谱.pdf 1. 格式打印 %03d&#xff1a;如果是两位数&#xff0c;将会在前面添上一位0 %.2f&#xff1a;会保留两位小数 如果是long&#xff0c;必须在数字后面加上L。 2. 进制转化 2.1. 十进制转任意进制&#xff1a; 十进制转任意进制时&#xff…