多线程(看这一篇就够了,超详细,满满的干货)

news2024/11/25 10:01:46

多线程

  • 一.认识线程(Thread)
      • 1. 1) 线程是什么
      • 1. 2) 为啥要有线程
      • 1.3) 进程和线程的区别
          • 标题1.4) Java的线程和操作系统线程的关系
  • 二.创建线程
      • 方法1:继承Thread类
      • 方法2:实现Runnable接口
      • 方法3:匿名内部类创建Thread子类对象
      • 标题方法4:匿名内部类创建Runnable子类对象
      • 方法5:lambda表达式创建Runnable子类对象
  • 三.Thread类及其方法
      • 3.1Thread的常见构造方法
      • 3.2Thread的几个常见属性
      • 3.3获取当前线程引用
      • 3.4休眠当前线程
  • 四:线程的状态
      • 4.1线程的所有状态
      • 4.2线程状态和状态转移的意义
      • 4.3观察线程的状态和转移
          • 示例1:
          • 示例2:
  • 五:多线程带来的的风险-线程安全(重点)
      • 5.1线程安全的概念
      • 5.2线程不安全的原因
      • 5.3线程的几大特性
          • 5.3.1:原子性
          • 5.3.2:可见性
          • 5.3.3:指令重排序

在这里插入图片描述

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

​​​​​​

一.认识线程(Thread)

1. 1) 线程是什么

⼀个线程就是⼀个 “执行流”. 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 “同时” 执行着多份代码,main()⼀般被称为主线程(Main Thread)。

1. 2) 为啥要有线程

首先, “并发编程” 成为 “刚需”.

  • 单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU 资源.

  • 有些任务场景需要 “等待 IO”, 为了让等待 IO 的时间能够去做⼀些其他的工作, 也需要用到并发编程. 其次,
    虽然多进程也能实现 并发编程, 但是线程比进程更轻量.

  • 创建线程比创建进程更快.

  • 销毁线程比销毁进程更快.

  • 调度线程比调度进程更快.

最后, 线程虽然比进程轻量, 但是人们还不满足, 于是又有了 “线程池”(ThreadPool) 和 “协程”(Coroutine)
关于线程池我们后面再介绍. 关于协程的话题我们此处暂时不做过多讨论.

1.3) 进程和线程的区别

  • 进程是包含线程的. 每个进程至少有⼀个线程存在,即主线程。
  • 进程和进程之间不共享内存空间. 同⼀个进程的线程之间共享同⼀个内存空间.
  • 进程是系统分配资源的最小单位,线程是系统调度的最小单位。
  • ⼀个进程挂了⼀般不会影响到其他进程. 但是⼀个线程挂了, 可能把同进程内的其他线程⼀起带走(整 个进程崩溃)
标题1.4) Java的线程和操作系统线程的关系

线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了⼀些API供用户使用(例如Linux的pthread库) 例如:Java标准库Thread的类可以视为是对操作系统提供的API进行了进⼀步的抽象和封装.

二.创建线程

方法1:继承Thread类

继承Thread来创建⼀个线程类,直接使用this就表示当前线程对象的引用

class MyThread extends Thread { 
    @Override
    public void run() {
        System.out.println("这⾥是线程运⾏的代码");
    }
}
public class Test {
    public static void main(String[] args)  {
        MyThread t = new MyThread();
        t.start();
    }
}

方法2:实现Runnable接口

实现Runnable接口,this表示的是 MyRunnable 的引用.需要使用Thread.currentThread()

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("这⾥是线程运⾏的代码");
    }
}
public class Test {
    public static void main(String[] args)  {
        Thread t = new Thread(new MyRunnable());
        t.start();
    }
}

方法3:匿名内部类创建Thread子类对象

public class Test {
    public static void main(String[] args)  {
        // 使⽤匿名类创建 Thread ⼦类对象
        Thread t1 = new Thread() {
            @Override
            public void run() {
                System.out.println("使⽤匿名类创建 Thread ⼦类对象");
            }
        };
    }
}

标题方法4:匿名内部类创建Runnable子类对象

public class Test {
    public static void main(String[] args)  {
        // 使⽤匿名类创建 Runnable ⼦类对象
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("使⽤匿名类创建 Runnable ⼦类对象");
            }
        });
    }
}

方法5:lambda表达式创建Runnable子类对象

public class Test {
    public static void main(String[] args)  {
        // 使⽤匿名类创建 Runnable ⼦类对象
        // 使⽤ lambda 表达式创建 Runnable ⼦类对象
        Thread t3 = new Thread(() -> System.out.println("使⽤匿名类创建 Thread ⼦类对象"));
        Thread t4 = new Thread(() -> {
            System.out.println("使⽤匿名类创建 Thread ⼦类对象");
        });
    }
}

三.Thread类及其方法

Thread 类是 JVM 用来管理线程的⼀个类,换句话说,每个线程都有⼀个唯⼀的 Thread 对象与之关联。而Thread 类的对象就是用来描述⼀个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。

3.1Thread的常见构造方法

在这里插入图片描述

Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

3.2Thread的几个常见属性

在这里插入图片描述

  • ID是线程的唯⼀标识,不同线程不会重复

  • 名称是各种调试工具用到

  • 状态表示线程当前所处的⼀个情况,下面我们会进⼀步说明

  • 优先级高的线程理论上来说更容易被调度到

关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有非后台线程结束后,才会结束运行。 是否存活,即简单的理解,为run方法是否运行结束了

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName());
                    Thread.sleep(1 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ": 我即将死去")
        });
        System.out.println(Thread.currentThread().getName() + ": ID: " + thread.getId());
        System.out.println(Thread.currentThread().getName() + ": 名称: " + thread.getName());
        System.out.println(Thread.currentThread().getName() + ": 状态: " + thread.getState());
        System.out.println(Thread.currentThread().getName() + ": 优先级: " + thread.getPriority());
        System.out.println(Thread.currentThread().getName() + ": 后台线程: " + thread.isDaemon());
        System.out.println(Thread.currentThread().getName() + ": 活着: " + thread.isAlive());
        System.out.println(Thread.currentThread().getName() + ": 被中断: " + thread.isInterrupted());
        thread.start();
    }
}
    

3.3获取当前线程引用

在这里插入图片描述

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
    }
}

3.4休眠当前线程

也是我们比较熟悉⼀组方法,有⼀点要记得,因为线程的调度是不可控的,所以,这个方法只能保证
实际休眠时间是大于等于参数设置的休眠时间的。
在这里插入图片描述

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis());
        Thread.sleep(3 * 1000);
        System.out.println(System.currentTimeMillis());
    }
}

四:线程的状态

4.1线程的所有状态

  • NEW:安排了工作,还未开始行动
  • RUNNABLE:可工作的.又可以分成正在工作中和即将开始工作.
  • BLOCKED:这几个都表示排队等着其他事情
  • WAITING:这几个都表示排队等着其他事情
  • TIMED_WAITING:这几个都表示排队等着其他事情
  • TERMINATED:工作完成了

4.2线程状态和状态转移的意义

在这里插入图片描述

4.3观察线程的状态和转移

示例1:

关注 NEW 、 RUNNABLE 、 TERMINATED 状态的转换

public class ThreadStateTransfer {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 1000_0000; i++) {
            }
        }, "李四");
        System.out.println(t.getName() + ": " + t.getState());;
        t.start();
        while (t.isAlive()) {
            System.out.println(t.getName() + ": " + t.getState());;
        }
        System.out.println(t.getName() + ": " + t.getState());;
    }
}
示例2:

关注 WAITING 、 BLOCKED 、 TIMED_WAITING 状态的转换

public static void main(String[] args) {
        final Object object = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    while (true) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("hehe");
                }
            }
        }, "t2");
        t2.start();
    }

使用jconsole可以看到t1的状态是TIMED_WAITING,t2的状态是BLOCKED

结论:

  • BLOCKED表示等待获取锁,WAITING和TIMED_WAITING表示等待其他线程发来通知.
  • TIMED_WAITING线程在等待唤醒,但设置了时限;WAITING线程在无限等待唤醒

五:多线程带来的的风险-线程安全(重点)

5.1线程安全的概念

想给出⼀个线程安全的确切定义是复杂的,但我们可以这样认为:如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。

5.2线程不安全的原因

线程调度是随机的,这是线程安全问题的罪魁祸首,随机调度使⼀个程序在多线程环境下,执行顺序存在很多的变数.程序猿必须保证在任意执行顺序下,代码都能正常工作.

5.3线程的几大特性

5.3.1:原子性

代码实现时不会受到其它线程的穿插执行,这样就保证了这段代码的原子性了。
有时也把这个现象叫做同步互斥,表示操作是互相排斥的。

5.3.2:可见性

⼀个线程对共享变量值的修改,能够及时地被其他线程看到.

Java内存模型(JMM):Java虚拟机规范中定义了Java内存模型.目的是屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到⼀致的并发效果.
在这里插入图片描述

  • 线程之间的共享变量存在主内存(Main Memory).
  • 每⼀个线程都有自己的"工作内存"(Working Memory)
  • 当线程要读取⼀个共享变量的时候,会先把变量从主内存拷贝到工作内存,再从工作内存读取数据.
  • 当线程要修改⼀个共享变量的时候,也会先修改工作内存中的副本,再同步回主内存,由于每个线程有自己的工作内存,这些工作内存中的内容相当于同⼀个共享变量的"副本".此时修改线程1的工作内存中的值,线程2的工作内存不⼀定会及时变化.
5.3.3:指令重排序

什么是代码重排序
⼀段代码是这样的:
1.去前台取下U盘
2. 去教室写10分钟作业
3. 去前台取下快递 如果是在单线程情况下,JVM、CPU指令集会对其进行优化,比如,按1->3->2的方式执行,也是没问 题,可以少跑⼀次前台。这种叫做指令重排序 编译器对于指令重排序的前提是"保持逻辑不发生变化".这⼀点在单线程环境下比较容易判断,但是 在多线程环境下就没那么容易了,多线程的代码执行复杂程度更高,编译器很难在编译阶段对代码的 执行效果进行预测,因此激进的重排序很容易导致优化后的逻辑和之前不等价. 重排序是⼀个比较复杂的话题,涉及到CPU以及编译器的⼀些底层工作原理,此处不做过多讨论

在这里插入图片描述
如果觉得文章不错,期待你的一键三连哦,你个鼓励是我创作的动力之源,让我们一起加油,顶峰相见*!!!💓 💓 💓*

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

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

相关文章

(循环依赖问题)学习spring的第九天

Bean实例的属性填充 Spring在属性注入时 , 分为如下几种情况 : 注入单向对象引用 : 如usersevice里注入userdao , userdao里没有注入其他属性 注入双向对象引用 : 如usersevice里注入userdao , userdao也注入usersevice属性 二 . 着重看循环依赖问题 (搞清原理即可) 问题提出…

基于Spring+mybatis+vue的在线课后测试系统(Java毕业设计)

大家好&#xff0c;我是DeBug&#xff0c;很高兴你能来阅读&#xff01;作为一名热爱编程的程序员&#xff0c;我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里&#xff0c;我将会结合实际项目经验&#xff0c;分享编程技巧、最佳实践以及解决问题的方法。无论你是…

C语言第四弹---printf和scanf详解

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 printf和scanf详解 1、printf和scanf详解介绍1.1 printf1.1.1 基本用法1.1.2 占位符1.1.3 占位符列举1.1.4 输出格式1.1.4.1 限定宽度1.1.4.2 总是显示正负号1.1…

响应式Web开发项目教程(HTML5+CSS3+Bootstrap)第2版 例4-6 fieldset

代码 <!doctype html> <html> <head> <meta charset"utf-8"> <title>fieldset</title> </head><body> <form action"#"><fieldset><legend>学生信息</legend>姓名&#xff1a;&…

svg矢量图标在wpf中的使用

在wpf应用程序开发中&#xff0c;为支持图标的矢量缩放&#xff0c;及在不同分辨率下界面中图标元素的矢量无损缩放&#xff0c;所以常常用到svg图标&#xff0c;那么如果完 美的将svg图标运用到wpf日常的项目开发中呢&#xff0c;这里分享一下我的个人使用经验和详细步骤。 步…

互联网加竞赛 基于机器视觉的手势检测和识别算法

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的手势检测与识别算法 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng…

1.10马原,总复习PART2

马克思主义鲜明特征 革命性&#xff0c;科学性&#xff0c;实践性 人民性&#xff0c;发展性 革命性、科学性&#xff0c;实践性&#xff0c;人民性&#xff0c;发展性 革命性&#xff0c;科学性&#xff0c;实践性&#xff0c;人 革命性&#xff0c;发展性&#xff0c…

Java设计模式-原型模式(3)

大家好,我是馆长!从今天开始馆长开始对java设计模式的创建型模式中的单例模式、原型模式、工厂方法、抽象工厂、建造者的原型模式进行讲解和说明。 原型模式(Prototype Pattern) 定义 原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对…

Mac book air 重新安装系统验证显示 untrusted_cert_title

环境&#xff1a; Mac Book Air macOS Sierra 问题描述&#xff1a; Mac book air 重新安装系统验证显示 untrusted_cert_title 解决方案&#xff1a; 1.终端输入命令行输入 date 会看到一个非常旧的日期 2.更改日期为当前时间 使用以下命令来设置日期和时间&#xff1a…

流式湖仓增强,Hologres + Flink构建企业级实时数仓

云布道师 2023 年 12 月&#xff0c;由阿里云主办的实时计算闭门会在北京举行&#xff0c;阿里云实时数仓Hologres 研发负责人姜伟华现场分享 HologresFlink 构建的企业级实时数仓&#xff0c;实现全链路的数据实时计算、实时写入、实时更新、实时查询。同时&#xff0c;随着流…

backtrader策略库:强化学习一: 梯度提升( Gradient Ascent)

本文来自博客文章&#xff0c;文末含源码链接。 In the next few posts, I will be going over a strategy that uses Machine Learning to determine what trades to execute. Before we start going over the strategy, we will go over one of the algorithms it uses: Gra…

数据结构一:算法效率分析(时间复杂度和空间复杂度)-重点

在学习具体的数据结构和算法之前&#xff0c;每一位初学者都要掌握一个技能&#xff0c;即善于运用时间复杂度和空间复杂度来衡量一个算法的运行效率。所谓算法&#xff0c;即解决问题的方法。同一个问题&#xff0c;使用不同的算法&#xff0c;虽然得到的结果相同&#xff0c;…

【c++leetcode】1913.Maximum Product Difference Between Two Pairs

问题入口 这个问题很容易解决。只要将数组排序&#xff0c;返回 最大元素*第二大元素-最小元素*第二小元素 即可。通过这道题顺便复习一些排序算法 。 直接使用sort函数 class Solution {public:int maxProductDifference(vector<int>& nums) {sort(nums.begin(),…

《WebKit 技术内幕》之三(2): WebKit 架构和模块

2.基于 Blink 的 Chrominum 浏览器结构 2.1 Chrominum 浏览器的架构及模块 Chromium也是基于WebKit&#xff08;Blink&#xff09;开发的&#xff0c;并且在WebKit的移植部分中&#xff0c;Chromium也做了很多有趣的事&#xff0c;所以通过Chromium可以了解如何基于WebKit构建浏…

尝试解决githubclone失败问题

BV1qV4y1m7PB 根据这个视频 似乎是我的linux的github似乎下好了 我没有配置好 比如我的ssh-key 现在根据视频试试 首先需要跳转到ssh的文件夹&#xff1a; cd ~/.ssh 然后生成一个ssh-key&#xff1a; ssh-keygen -t rsa -C "<github资料里的邮箱>" 然后…

Linux之进程间通信(管道)

目录 一、进程间通信 1、进程间通信的概念 2、进程间通信的目的 3、进程间通信的分类 二、管道 1、管道基本介绍 2、匿名管道 3、命名管道 一、进程间通信 1、进程间通信的概念 什么是进程间通信&#xff1f; 我们在学习了进程的相关知识后&#xff0c;知道&#xff…

java SSM网上小卖部管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM网上小卖部管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源 代码和数据库&#xff0c;系统主要…

生物信息基础:pysam读写基因组文件

Pysam[1]是一个 Python 模块&#xff0c;它打包了高通量测序库htslib[2]的 C-API&#xff0c;可用于读写基因组相关文件&#xff0c;如 Fasta/Fastq&#xff0c;SAM/BAM/CRAM&#xff0c;VCF 等。本文以 Fasta/Fastq 文件的读写为例&#xff0c;介绍 Pysam 的用法&#xff0c;详…

更新Ubuntu并同步网络时间

ubuntu环境搭建专栏&#x1f517;点击跳转 Ubuntu系统环境搭建&#xff08;九&#xff09;——更新Ubuntu并同步网络时间 文章目录 Ubuntu系统环境搭建&#xff08;九&#xff09;——更新Ubuntu并同步网络时间1.更新Ubuntu1.1 查看ubuntu版本和详细信息1.2 创建root用户1.3 更…

Maven error in opening zip file?maven源码debug定位问题jar包

文章目录 问题发现调试Maven1. 查看maven版本2. 下载对应版本的maven源码3. 打开maven源码&#xff0c;配置启动选项 启动maven debug模式进入maven 源码&#xff0c;打断点调试找jar包算账 已录制视频 视频连接 问题发现 最近使用maven分析jar包的时候遇到了一个很搞的问题。…