第十七章 多线程基础

一、线程相关概念

1. 程序

程序:是为完成特定任务、用某种语言编写的一组指令的集合。
简单说就是代码。

2. 进程

(1)进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统会为该进程分配内存空间。
(2)进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程,有它
自身的产生、存在和消亡的过程。

3. 线程

(1)线程由进程创建,是进程的一个实体。
(2)一个进程可以拥有多个线程。

(1)单线程:同一时刻,只允许执行一个线程。
(2)多线程:同一时刻,可以执行多个线程。比如:一个QQ进程,可以同时打开多个聊天窗口;一个迅雷进程,可以同时下载多个文件。

(1)并发:同一时刻,多个任务交替执行。单核 cpu 实现的多任务就是并发。
(2)并行:同一时刻,多个任务同时执行。多核 cpu 可以实现并行。

二、线程基本使用

1. 创建线程的两种方式

在 Java 中线程使用有两种方法
(1)继承 Thread 类,重写 run 方法。
(2)实现 Runnable 接口,重写 run 方法。
在这里插入图片描述

2. 线程应用1 - 继承 Thread 类

public class Demo extends Thread{

    public static void main(String[] args) {

        /*
        public synchronized void start() {
            start0();
        }
        private native void start0();
        start0() 是native方法,是 JVM 调用, 底层是 c/c++实现
         */
        Demo demo = new Demo();
        demo.start();
    }

    @Override
    public void run() {
        // 重写run方法
    }
}

【start()】方法调用【start0()】方法后,该线程并不一定会立马执行,只是将线程变成了可运行状态。具体什么时候执行,取决于 CPU,由 CPU 统一调度。

3. 线程应用2 - 实现 Runnable 接口

Java 是单继承的,在某些情况下一个类可能已经继承了某个父类,既不能再继承 Thread。

public class Demo implements Runnable{

    public static void main(String[] args) {

        Demo demo = new Demo();
        Thread thread = new Thread(demo);
        thread.start();
    }

    @Override
    public void run() {
        // 重写run方法
    }
}

三、继承 Thread vs 实现 Runnable 的区别

(1)从 Java 的设计来看,通过继承 Thread 或者实现 Runnable 接口来创建线程本质上没有区别,从 jdk 帮助文档我们可以看到 Thread 类本身就实现了 Runnable 接口。
(2)实现 Runnable 接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用 Runnable。

四、线程终止

基本说明
(1)当线程完成任务后,会自动退出。
(2)还可以通过 使用变量 来控制 run 方法退出的方式停止线程,即 通知方式

public class Demo {
    public static void main(String[] args) {
        AThread at = new AThread();
        new Thread(at).start();
        
        at.setLoop(false);
    }
}

class AThread implements Runnable{

    // 步骤1:定义标记变量,默认为true
    boolean loop = true;

    @Override
    public void run() {
        // 步骤2:将loop作为循环条件
        while (loop){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // 步骤3:提供公共的set方法,用于更新loop
    public void setLoop(boolean loop) {
        this.loop = loop;
    }
}

五、线程常用方法

1.常用方法第一组

(1)setName:设置线程名称,使之与参数 name 相同
(2)getName:返回该线程的名称
(3)start:使该线程开始执行;Java 虚拟机底层调用该线程的【start0()】方法
(4)run:调用线程对象 run 方法
(5)setRriority:更改线程的优先级
(6)getPriority:获取线程的优先级
(7)sleep:在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
(8)interrupt:中断线程

注意事项和细节:
(1)start 底层会创建新的线程,调用 run 方法。
(2)线程优先级的范围:1、5、10。
(3)interrupt,中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程。
(4)sleep:线程的静态方法,使当前线程休眠。


    /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;
public class Demo {
    public static void main(String[] args) {
        T t = new T();
        t.start();

        // 中断
        t.interrupt();
    }
}

class T extends Thread{

    @Override
    public void run() {

        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            // 当该线程执行到一个interrupt方法时,就会catch一个异常。加入业务代码。
            System.out.println("被interrupt了");
            e.printStackTrace();
        }
    }
}

2.常用方法第二组

(1)yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功。
(2)join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务。

案例:main 线程创建一个子线程,每隔 1s 输出 hello,输出20次,主线程每隔1秒,输出hi,输出20次。要求两个线程同时执行,当主线程输出5次后,就让子线程运行完毕,主线程再继续。

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();
        for (int i = 0; i < 20; i++) {
            Thread.sleep(1000);
            System.out.println("hi"+i);
            if (i==5){
                // 让子线程插队执行完成
                t.join();
            }
        }
    }
}

class T extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("hello"+i);
        }
    }
}

3. 用户线程和守护线程

用户线程和守护线程:
(1)用户线程:也叫工作线程,当线程的任务执行完或通知方式结束。
(2)守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。
(3)常见的守护线程:垃圾回收机制。

public class Demo {
    public static void main(String[] args) {
        T t = new T();
        // 设置为守护线程
        t.setDaemon(true);
        t.start();
    }
}

六、线程的生命周期

1. JDK 中用 Thread.State 枚举表示了线程的状态

在这里插入图片描述
在这里插入图片描述

七、Synchronized

1. 线程同步机制

(1)在多线程编程中,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
(2)线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。

2. 同步具体方法-Synchronized

(1)同步代码块

synchronized(对象){ // 得到对象的锁,才能操作同步代码
	// 需要被同步代码;
}

(2)Synchronized 还可以放在方法声明中,表示整个方法-为同步方法

public synchronized void m(String name){
	// 需要被同步代码;
}

八、互斥锁

(1)1.Java语言中,引入了对象互压锁的概念,来保证共享数据操作的完整性。
(2)每个对象都对应于一个可称为“互序锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
(3)关键字 synchronized 来与对象的互压锁联系。当某个对象用 synchronized 修饰时,表明该对象在任一时刻只能由一个线程访问。
(4)同步的局限性:导致程序的执行效率要降低。
(5)同步方法(非静态的)的锁可以是 this,也可以是其他对象(要求是同一个对象)。
(6)同步方法(静态的)的锁为当前类本身。

注意事项和细节
(1)同步方法如果没有使用 static 修饰:默认锁对象为 this。
(2)如果方法使用 static 修饰,默认锁对象:当前类.class。
(3)实现步骤:
1️⃣需要先分析上锁的代码
2️⃣选择同步代码块(优先)或同步方法
3️⃣要求多个线程的锁对象为同一个!

九、线程的死锁

基本介绍
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生。

public class Demo {

    public static void main(String[] args) {
        //模拟死锁现象
        DeadLockDemo A = new DeadLockDemo(true);
        A.setName("A 线程");
        DeadLockDemo B = new DeadLockDemo(false);
        B.setName("B 线程");
        A.start();
        B.start();
    }

}


class DeadLockDemo extends Thread {
    static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用 static
    static Object o2 = new Object();
    boolean flag;

    public DeadLockDemo(boolean flag) {//构造器
        this.flag = flag;
    }

    @Override
    public void run() {
        //下面业务逻辑的分析
        //1. 如果 flag 为 T, 线程 A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁
        //2. 如果线程 A 得不到 o2 对象锁,就会 Blocked
        //3. 如果 flag 为 F, 线程 B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁
        //4. 如果线程 B 得不到 o1 对象锁,就会 Blocked
        if (flag) {
            synchronized (o1) {//对象互斥锁, 下面就是同步代码
                System.out.println(Thread.currentThread().getName() + " 进入 1");
                synchronized (o2) { // 这里获得 li 对象的监视权
                    System.out.println(Thread.currentThread().getName() + " 进入 2");
                }
            }
        } else {
            synchronized (o2) {
                System.out.println(Thread.currentThread().getName() + " 进入 3");
                synchronized (o1) { // 这里获得 li 对象的监视权
                    System.out.println(Thread.currentThread().getName() + " 进入 4");
                }
            }
        }
    }
}

十、释放锁

1. 下面操作会释放锁

(1)当前线程的同步方法、同步代码块执行结束
案例:上厕所,完事出来
(2)当前线程在同步代码块、同步方法中遇到 break、return。
案例:没有正常的完事,经理叫他修改bug,不得已出来
(3)当前线程在同步代码块、同步方法中出现了未处理的 Error 或 Exception,导致异常结束
案例:没有正常的完事,发现忘带纸,不得已出来
(4)当前线程在同步代码块、同步方法中执行了线程对象的 wait()方法,当前线程暂停,并释放锁。
案例:没有正常完事,觉得需要配酿下,所以出来等会再进去

2. 下面操作不会释放锁

(1)线程执行同步代码块或同步方法时,程序调用 Thread.sleep()、Thread.yield() 方法暂停当前线程的执行,不会释放锁
案例:上厕所,太困了,在坑位上胖了一会
(2)线程执行同步代码块时,其他线程调用了该线程的 suspend() 方法将该线程挂起,该线程不会释放锁
提示:应尽量避免使用 suspend()和 resume() 来控制线程,方法不再推荐使用

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

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

相关文章

离职的赔偿算法n 、n+1、2n你还不懂嘛?

亲爱的小伙伴们&#xff0c;由于微信公众号改版&#xff0c;打乱了发布时间&#xff0c;为了保证大家可以及时收到文章的推送&#xff0c;可以点击上方蓝字关注测试工程师成长之路&#xff0c;并设为星标就可以第一时间收到推送哦&#xff01; 哪些情况需要支付经济补偿? 劳动…

看图了解ODF光纤配线架,详细熔接过程学习

弱电工程&#xff0c;远距离传输离不开光纤&#xff0c;只有光纤才能让网络传输的更远&#xff0c;今天了解光纤的配套产品&#xff0c;光纤配线架&#xff08;Optical Distribution Frame&#xff09;用于光纤通信系统中局端主干光缆的成端和分配&#xff0c;可方便地实现光纤…

【Python】requests库在CTFWeb题中的应用

目录 ①Bugku-GET ②Bugku-POST ③实验吧-天下武功唯快不破 ④Bugku-速度要快 ⑤Bugku-秋名山车神 ⑥Bugku-cookies ①Bugku-GET import requestsresprequests.get(urlhttp://114.67.175.224:12922/,params{what:flag}) print(resp.text)//或者 //resprequests.get(urlht…

使用Visual Studio调试VisionPro脚本

使用Visual Studio调试VisionPro脚本 方法一 &#xff1a; 修改项目文件 csproj步骤&#xff1a; 方法二 &#xff1a; Visual Studio附加功能步骤&#xff1a; 方法一 &#xff1a; 修改项目文件 csproj 步骤&#xff1a; 开启VisionPro脚本调试功能 创建一个VisionPro程序…

VBA技术资料MF99:在代码中使用VLookUp函数

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

华为数通方向HCIP-DataCom H12-831题库(多选题:221-240)

第221题 在割接项目的项目调研阶段需要对现网硬件环境进行观察,主要包括以下哪些内容? A、设备的位置 B、ODF位置 C、接口标识 D、光纤接口对应关系 答案:ABCD 解析: 在项目割接前提的项目调研阶段,需要记录下尽可能详细的信息。 第222题 以下哪些项能被正则表达式10*成…

每日一题——LeetCode206.反转链表

个人主页&#xff1a;白日依山璟 专栏&#xff1a;Java|数据结构与算法|每日一题 文章目录 1. 题目描述示例1示例2示例3提示 2. 思路3.代码 1. 题目描述 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例1 输入&#xff1a;head [1…

<JavaEE> TCP 的通信机制 -- 确认应答 和 超时重传

目录 TCP的通信机制的核心特性 一、确认应答 1&#xff09;什么是确认应答&#xff1f; 2&#xff09;如何“确认”&#xff1f; 3&#xff09;如何“应答”&#xff1f; 二、超时重传 1&#xff09;丢包的概念 2&#xff09;什么是超时重传&#xff1f; 3&#xff09…

算法leetcode|94. 二叉树的中序遍历(多语言实现)

文章目录 94. 二叉树的中序遍历&#xff1a;样例 1&#xff1a;样例 2&#xff1a;样例 3&#xff1a;提示&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 94. 二叉树的中序遍历&#xff1a; …

什么样的产品适合建设私域流量池?

私域流量是什么&#xff1f;公域流量和私域流量是相对概念&#xff0c;触达和运营更加自由的流量称为私域流 量。私域流量的形态&#xff0c;我们称为私域流量池。私域流量池本质是便捷和低成本的运营方式&#xff0c; 运营用户&#xff0c;追求更便宜的流量来源、更高的售卖转…

什么是动态IP?静态IP和动态IP有什么区别?

动态IP(Dynamic IP)和静态IP(Static IP)它是指在计算机网络中分配给设备的两种不同类型的IP地址。 动态IP是指每次设备连接到网络时&#xff0c;网络服务提供商(ISP)IP地址的动态分配。当设备重新连接到网络时&#xff0c;它可能会被分配到不同的IP地址。动态IP适用于传统的家…

通过IntersectionObserver实现图片下拉加载(瀑布流布局)

效果展示&#xff1a; 瀑布流上拉加载 目录结构 html: <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><meta name"vie…

力扣刷题记录(18)LeetCode:474、518、377、322

目录 474. 一和零 518. 零钱兑换 II 377. 组合总和 Ⅳ 322. 零钱兑换 总结&#xff1a; 474. 一和零 这道题和前面的思路一样&#xff0c;就是需要将背包扩展到二维。 class Solution { public:int findMaxForm(vector<string>& strs, int m, int n) {vector&l…

KNN与KD树博客总结

目录 总结小结&#xff1a; 总结 原始篇&#xff1a;KNN算法及其优缺点算法思想改进篇&#xff1a;KD树&#xff08;KNN的plus版算法实现第一篇&#xff1a;平衡二叉树的构建&#xff08;递归算法实现第二篇&#xff1a;KD树的构建&#xff08;递归算法实现第三篇&#xff1a;…

开源持续测试平台Linux MeterSphere本地部署与远程访问

文章目录 前言1. 安装MeterSphere2. 本地访问MeterSphere3. 安装 cpolar内网穿透软件4. 配置MeterSphere公网访问地址5. 公网远程访问MeterSphere6. 固定MeterSphere公网地址 前言 MeterSphere 是一站式开源持续测试平台, 涵盖测试跟踪、接口测试、UI 测试和性能测试等功能&am…

windows安全配置实验手册

访问控制策略&#xff08;L1940520022J&#xff09; 预备知识 Windows 7中&#xff0c;不仅有面向软件的限制方法&#xff0c;还增加了一种名为AppLocker的访问控制策略&#xff08;仅适用于企业版和旗舰版&#xff09;。 实验环境 操作系统类型&#xff1a;windows 7。 实…

【问题记录-ASIO】Audition多通道ASIO驱动提示“(不工作)“问题

一&#xff0c;问题现象 在使用Audition打开多通道工程&#xff0c;使用ASIO驱动&#xff0c;却发现设备提示“不工作”&#xff0c;如下图所示&#xff1a; 二&#xff0c;解决方法 2.1 声卡设备枚举确认 首先确认声卡设备枚举是否成功&#xff0c;如果没有枚举成功&…

【SpringCloud笔记】(10)消息总线之Bus

Bus 前言 戳我了解Config 学习Config中我们遇到了一个问题&#xff1a; 当我们修改了GitHub上配置文件内容&#xff0c;微服务需要配置动态刷新并且需要手动向客户端发送post请求刷新微服务之后才能获取到GitHub修改过后的内容 假如有多个微服务客户端3355/3366/3377…等等…

将本地镜像推送到阿里云

文章目录 创建仓库镜像登录 并上传下载上传的 创建仓库镜像 利用下面的脚本进行配置 登录 并上传 [roothadoop100 ~]# docker login --username13thm registry.cn-hangzhou.aliyuncs.com Password: [roothadoop100 ~]# docker tag ba78e6d6845c registry-vpc.cn-hangzhou.al…

C++-类和对象(1)

1.面向过程和面向对象初步认识 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题。 C是基于面向对象的&#xff0c;关注的是对象&#xff0c;将一件事情拆分成不同的对象&#xff0c;靠对象之间的交互完 成。…