多线程基础说明【基础篇】

news2024/10/2 22:19:37

目录

🌭1.相关概念 

🍿2.创建和启动线程 

🥞3.线程安全

🧈4.死锁

🥓5.线程通信的方法


1.相关概念

1.1程序

为完成特定任务,用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。

1.2进程

  • 程序的一次执行过程,或是正在内存中运行的应用程序。
  • 每个进程都有一个独立的内存空间。
  • 程序是静态的,进程是动态的。
  • 进程是操作系统调度和分配资源的最小单位。

1.3线程

  • 进程可进一步细化为线程,是程序内部的一条执行路径。
  • 一个进程中至少有一个线程。
  • 一个进程同一时间若 并行 执行多个线程,就是支持多线程的。
  • 线程作为 CPU调度和执行的最小单位。

1.4并行

  • 指两个或多个事件在 同一时刻 发生(同时发生)。
  • 指在同一时刻,有多条指令 在多个CPU 上 同时 执行。

比如:多个人同时做不同的事。

1.5并发 

  • 指两个或多个事件在同一个时间段内发生。
  • 即在一段时间内,有多条指令在单个CPU上快速轮换、交替执行,使得在宏观上目有名个进程同时执行的效果

2.创建和启动线程 

Java语言的JvM允许程序运行多个线程,使用java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例

2.1继承Thread类

  • 1.创建一个类,继承Thread
  • 2.重写Thread类的run()方法,将此线程要执行的操作,声明在此方法中
  • 3.创建当前Thread的子类的对象
  • 4.通过对象调用start(), start():1.启动线程,2.调用当前线程的run()方法
public class MyTest {
    public static void main(String[] args) {

        //3.创建当前Thread的子类的对象
        PrintNumber p1 = new PrintNumber();


        //4.调用start()方法
        p1.start();

        //main()所在线程执行的操作
        for (int i=0;i<=100;i++){
            if (i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }


}

//1.创建子类,继承Thread
class PrintNumber extends Thread {
    //2.重写run()方法
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}
  • 不能使用p1.run()替换p1.start()的调用 
  • 不能让已经执行start()的线程,再次执行start(),否则报错,应重新创建对象再调用start()

 2.2实现Runnable接口

  • 1.创建一个类,实现Runnable接口
  • 2.实现接口中的run()方法,将此线程要执行的操作,写在run()方法中
  • 3.创建当前实现类的对象
  • 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例
  • 5.Thread类的实例调用start() 
public class RunnableTest {
    public static void main(String[] args) {

        //3.创建实现类的对象
        Number n1 = new Number();

        //4.将对象传递到Thread的构造器中
        Thread thread = new Thread(n1);

        //5.调用start()方法
        thread.start();

    }
}



//1.创建类,实现Runnable接口
class Number implements Runnable {
    //2.实现run()方法,并将要执行的操作,写在run()方法中
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() +"-偶数:"+i);
            }
        }
    }
}

也可以使用匿名实现RUnnable接口,

new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <= 100; i++) {
                    if (i % 2 == 0) {
                        System.out.println(Thread.currentThread().getName() + "-偶数:" + i);
                    }
                }
            }
        }).start();

使用Runnable的好处:

  • 1.实现的方式,避免类的单继承的局限性
  • 2.更适合处理共享数据的问题
  • 3.实现了代码和数据的分类

2.3常用方法

  • 1.start():1.启动线程 2.调用线程的run()方法
  • 2.run():将线程要执行的操作,写在run()方法中 
  • 3.currentThread():获取当前代码执行所对应的线程
  • 4.sleep():静态方法,睡眠指定毫秒数
  • 5.yield():静态方法,释放cpu的执行权
  • 6.jion():在线程A中通过线程B调用jion(),线程A进入阻塞状态直到线程B执行完成。
  • 7.isAlive():判断当前线程是否存活 
  •  2.4.线程的生命周期

3.线程安全

3.1问题

模拟三个窗口卖票,在第一个线程未结束的同时,其他线程参与进来,会出现重复票和错票

3.2解决

必须保证一个线程a在操作共享数据的过程中,其他线必须等待,直到线程a操作结束,其他线程才可以继续操作

3.3线程的同步机制

  • 方式一:同步代码块

synchronized (同步监视器){

      操作共享数据的代码

}

  1. 共享数据:多个线程需要操作的数据。
  2. 同步监视器:俗称 锁  。哪个线程获取了锁,哪个线程就能执行代码块。
  3. 同步监视器可以使用任何一个类的对象充当,当多个线程必须共用同一个同步监视器。
public class WindowTest {

    public static void main(String[] args) {
        SaleTicket saleTicket = new SaleTicket();

        Thread t1 = new Thread(saleTicket);
        Thread t2 = new Thread(saleTicket);
        Thread t3 = new Thread(saleTicket);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");


        t1.start();
        t2.start();
        t3.start();
    }

}


class SaleTicket implements Runnable {

    int ticket = 100;
    Object object = new Object();

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (object) {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "售票,票号:" + ticket);
                    ticket--;
                } else {
                    System.out.println(Thread.currentThread().getName()+"票数不足!");
                    break;
                }
            }
        }

    }
}
  • 方式二:同步方法
public class WindowTest {

    public static void main(String[] args) {
        SaleTicket saleTicket = new SaleTicket();

        Thread t1 = new Thread(saleTicket);
        Thread t2 = new Thread(saleTicket);
        Thread t3 = new Thread(saleTicket);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");


        t1.start();
        t2.start();
        t3.start();
    }

}


class SaleTicket implements Runnable {

    static int ticket = 100;
    static Object object = new Object();
    Boolean isFlag=true;

    @Override
    public void run() {
        while (isFlag) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
                show();

        }
    }

    public synchronized void show(){ //此时的同步监视器就是this,即代码中的saleTicket,唯一的
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "售票,票号:" + ticket);
            ticket--;
        }else {
            System.out.println(Thread.currentThread().getName()+"票数不足");
            isFlag=false;
        }
    }
}

注:

非静态的同步方法,默认同步监视器是this

静态的同步方法,默认同步监视器是当前类本身

synchronized

好处:解决了线程安全问题

弊端:在操作共享数据时,多线程其实是串行执行的,性能较低 

4.死锁

不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。

4.1原因

  • 互斥条件
  • 占用且等待
  • 不可抢夺(或不可抢占)
  • 循环等待

4.2解决

  • 针对条件1:互斥条件基本上无法被破坏。因为线程需要通过互斥解决安全问题。
  • 针对条件2:可以考虑一次性申请所有所需的资源,这样就不存在等待的问题。
  • 针对条件3:占用部分资源的线程在进一步申请其他资源时,如果申请不到,就主动释放掉已经占用的资源。
  • 针对条件4:可以将资源改为线性顺序。申请资源时,先申请序号较小的,这样避免循环等待问题。 

5.线程通信的方法

  • wait():线程一旦执行此方法,就进入等待状态。同时,会释放对同步监视器的调用
  • notify():一但执行此方法,就会唤醒wait()的线程中优先级最高的那个线程,如果被wait()唤醒的线程优先级相同,则随即唤醒一个
  • notifyAll():一但执行此方法,就唤醒所有被wait的线程
public class AdTest {

    public static void main(String[] args) {
        PrintNumbers printNumbers = new PrintNumbers();

        Thread thread1 = new Thread(printNumbers, "线程1");
        Thread thread2 = new Thread(printNumbers, "线程2");

        thread1.start();
        thread2.start();
    }

}

class PrintNumbers implements Runnable {

    private int number = 1;

    @Override
    public void run() {

        while (true) {
            synchronized (this) {
                notify();
                if (number <= 100) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;

                    try {
                        wait();//线程执行此方法,进入等待状态,同时释放同步监视器的调用
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    break;
                }
            }
        }
    }
}

wait()  VS  sleep()

相同点:一旦执行,当前线程都会阻塞

不同点:声明的位置、使用场景、sleep不会释放同步监视器、阻塞的方式

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

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

相关文章

Flask基础学习3

参考视频&#xff1a;41-【实战】答案列表的渲染_哔哩哔哩_bilibili flask 实现发送短信功能 pip install flask-mail # 安装依赖 我这里用登录的网易邮箱获取的授权码&#xff08;登录QQ邮箱的授权码总是断开收不到邮件&#xff09;&#xff0c; # config # config mail MAI…

vue3 实现 el-pagination页面分页组件的封装以及调用

示例图 一、组件代码 <template><el-config-provider :locale"zhCn"><el-pagination background class"lj-paging" layout"prev, pager, next, jumper" :pager-count"5" :total"total":current-page"p…

LeetCode二叉树中的第 K 大层和

题目描述 给你一棵二叉树的根节点 root 和一个正整数 k 。 树中的 层和 是指 同一层 上节点值的总和。 返回树中第 k 大的层和&#xff08;不一定不同&#xff09;。如果树少于 k 层&#xff0c;则返回 -1 。 注意&#xff0c;如果两个节点与根节点的距离相同&#xff0c;则…

SocketWeb实现小小聊天室

SocketWeb实现小小聊天室 消息推送的常见方式轮询长轮询SSE&#xff08;server-sent event&#xff09;&#xff1a;服务器发送事件WebSocketWebSocket简介WebSocket API 实现小小聊天室实现流程消息格式客户端-->服务端服务端-->客户端 消息推送的常见方式 轮询 浏览器…

C# TesseractOCR识别身份证号

https://github.com/tesseract-ocr/tessdata 新建控制台项目并添加包 Tesseract和Tesseract.Drawing 下载训练的模型 地址 代码实现 using Tesseract;var filePath "F:\\Desktop\\韦小宝.png"; var exePath AppDomain.CurrentDomain.BaseDirectory; var …

远程连接Redis

以连接阿里云上的Redis为例 1. 在阿里云安全组中开放端口 2.修改Redis启动时所用的配置文件&#xff08;redis.conf&#xff09; 2.1 修改ip地址 如图&#xff1a;将默认的本地ip bind 127.0.0.1地址改为bind 0.0.0.0 2.2 将保护模式关闭 将默认的 supervised yes 改为 n…

Docker基础(一)

文章目录 1. 基础概念2. 安装docker3. docker常用命令3.1 帮助命令3.2 镜像命令3.3 容器命令3.4 其他命令 4. 使用案例 1. 基础概念 镜像&#xff08;Image&#xff09;&#xff1a;Docker 镜像&#xff08;Image&#xff09;&#xff0c;就相当于是一个 root 文件系统。比如官…

Java Web(八)--Servlet(一)

介绍 官网&#xff1a;Servlet 3.1 API Documentation - Apache Tomcat 8.0.53 为什么需要&#xff1f; 提出需求: 请用你现有的html css javascript&#xff0c;开发网站&#xff0c;比如可以让用户留言/购物/支付? 引入我们动态网页(能和用户交互)技术>Servlet 是什…

医院LIS(全称Laboratory Information Management System)系统源码

目录 一、医院LIS系统概况 二、医院LIS系统建设必要性 三、为什么要使用LIS系统 四、技术框架 &#xff08;1&#xff09;总体框架 &#xff08;2&#xff09;技术细节 &#xff08;3&#xff09;LIS主要功能模块 五、LIS系统优势 &#xff08;1&#xff09;客户/用户…

【Python】记录生产编程小tips(字符串处理、列表删除、字典、csv、excel操作)持续更新

文章目录 一.for循环删除列表元素1.删除list元素方法2.直接删除往往结果与期望不一致3.循环删除元素的正确方法&#xff1a;方法1&#xff1a;while循环i减方法2&#xff1a;使用copy方法3&#xff1a;倒叙遍历 二.字符串处理库三.字典—dict运用四.csv操作五.excel操作1.读exc…

AI:139-基于深度学习的语音指令识别与执行

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带关键代码,详细讲解供大家学习,希望…

改进 RAG:自查询检索

原文地址&#xff1a;Improving RAG: Self Querying Retrieval 2024 年 2 月 11 日 让我们来解决构建 RAG 系统时的一个大问题。 我们不能依赖语义搜索来完成每个检索任务。只有当我们追求单词的含义和意图时&#xff0c;语义搜索才有意义。 But in case&#xff0c;我们正…

python自动化管理和zabbix监控网络设备(有线网络和路由配置部分)

目录 目录 一、拓扑图 二、core-sw1 三、core-sw2 四、sum-sw1 五、sum-sw2 六、FW1 七、DMZ-SW1 结语 一、拓扑图 二、core-sw1 sys sysname core-sw1 vlan batch 10 20 30 40 50 60 100 vlan batch 200 210 220 230 240 250 stp region-configuration region-name…

选择稳定代理IP需要考虑哪些因素?

很多人不知道怎么选择稳定的代理IP&#xff0c;也不知道当前代理IP套餐是否稳定&#xff0c;今天我们一起来分析下&#xff0c;选择稳定的代理IP可以从哪几方面去考虑。 1、代理IP的可用性 代理IP要稳定&#xff0c;先要可以用。一个代理IP池是否稳定&#xff0c;要看代理IP的…

区分服务 DiffServ

目录 区分服务 DiffServ 区分服务的基本概念 区分服务 DiffServ 的要点 每跳行为 PHB DiffServ 定义的两种 PHB 区分服务 DiffServ 区分服务的基本概念 由于综合服务 IntServ 和资源预留协议 RSVP 都较复杂&#xff0c;很难在大规模的网络中实现&#xff0c;因此 IET…

Docker复习笔记

Centos7安装Docker Docker官网:www.docker.com Docker官网仓库:hub.docker.com Docker文档是比较详细的 安装相关依赖 yum -y install gcc gcc-c yum install -y yum-utils 设置docker镜像仓库 yum-config-manager --add-repo https://download.docker.com/linux/centos/do…

Mysql运维篇(五) 部署MHA--主机环境配置

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。如有侵权&#xff0c;请留言&#xff0c;我及时删除&#xff01; 大佬博文 https://www.cnblogs.com/gomysql/p/3675429.html MySQL 高可用&#xff08;MHA&#x…

js:通过input标签或Drag拖拽文件实现浏览器文件上传获取File文件对象

文档 https://developer.mozilla.org/zh-CN/docs/Web/API/Filehttps://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement/drag_event 通过读取文件可以获取File对象的信息 lastModified: 1707210706000 lastModifiedDate: Tue Feb 06 2024 17:11:46 GMT0800 (中国标准…

使用 Verilog 做一个可编程数字延迟定时器 LS7211-7212

今天的项目是在 Verilog HDL 中实现可编程数字延迟定时器。完整呈现了延迟定时器的 Verilog 代码。 所实现的数字延迟定时器是 CMOS IC LS7212&#xff0c;用于生成可编程延迟。延迟定时器的规格可以在这里轻松找到。基本上&#xff0c;延迟定时器有 4 种操作模式&#xff1a;…

备考北京高考数学:历年选择题真题练一练和解析(2014-2023)

还有三个多月的时间就要高考了&#xff0c;我们今天继续看北京市高考数学真题和解析。今天看5道选择题。独家制作的在线练习集&#xff0c;可以便捷地反复刷这些真题&#xff0c;吃透真题&#xff08;背后的知识点和解题技巧&#xff09;&#xff0c;让高考数学再上一个台阶。 …