【JavaEE初阶】多线程(一)

news2024/9/22 9:57:22

摄影分享!
在这里插入图片描述
在这里插入图片描述

文章目录

  • 认识线程(Thread)
    • 概念
    • 执行多线程编程
    • 创建线程的写法
      • 1.继承Thread,重写run
      • 2.实现Runnable接口
      • 3.使用匿名内部类,继承Thread
      • 4.使用匿名内部类,实现Runable
      • 5.使用Lambda表达式
    • Thread用法
    • Thread的几个常见属性
    • 启动一个线程-start()
    • 线程终止
    • 等待一个线程join()
    • 获取当前线程的引用

认识线程(Thread)

概念

引入“进程”这个概念,主要是为了解决“并发编程”的问题。
其实,多进程编程,已经可以解决并发编程的问题了。但是由于进程太重(消耗资源多,速度慢)。创建,销毁,调度一个进程的开销都比较大。主要是重在了“资源分配/回收”上。所以线程应运而生。线程也叫做“轻量级进程”解决并发编程问题的前提下,让创建,销毁,调度的速度更快一些。线程通过把申请资源/释放资源的操作省去所以线程更

举个例子~
在这里插入图片描述
在以上方案中,显然,方案2比方案1的成本要小很多。场地和物流线都可以使用之前的。这就是多线程版本的方案。此时,只是搞第一套生产线的时候,需要把资源申请到位,后续再加新的生产线,此时就复用之前的资源即可。
**线程和进程之间的关系,是进程包含线程。**一个进程可以包含一个线程,也可以包含多个线程(不能没有)。只有第一个线程启动的时候,开销是比较大的,后续的线程开销就小了。同一个进程里的多个线程之间,公用了进程的同一份资源。(主要指的是内存和文件描述符表)
如果每个进程由多个线程了,每个线程是独立在CPU上调度的。(线程是操作系统执行的基本单位)
每个线程也有自己的执行逻辑(执行流)
在这里插入图片描述
一个线程也是通过一个PCB来描述的。一个进程里可能是对应一个PCB,也可能是对应多个PCB。在同一个进程里的PCB之间,PID是一样的。内存指针和文件描述符表也是一样的。

再举个例子~

在这里插入图片描述
什么时候会出现这种安全问题呢?
多个执行流访问同一个共享资源的时候。
线程模型,天然就是资源共享的,多线程争抢同一个资源(同一个变量)非常容易触发的。
进程模型,天然是资源隔离的,不容易触发,进行进程间通信的时候,多个进程访问同一个资源,就可能会出问题。
多线程会提高效率,但不如多进程安全。

执行多线程编程

本身关于线程的操作,操作系统提供API。
Java是一个跨平台的语言,很多操作系统的提供的功能,都被JVM封装好了。由于Java在不同的操作系统上,实现了不同版本的JVM,所以Java实现了跨平台。
Java操作多线程,最核心的类是Thread。

class MyTread extends Thread{
    @Override
    public void run() {
        System.out.println("hello run");
    }
}
public class TreadDemo {
    public static void main(String[] args) {
        Thread t = new MyTread();
        t.start();
    }
}

在这里插入图片描述

t.start();

中start创建了一个新的线程。start中没有调用run。而是创建了一个线程,由线程来执行run方法。
就是调用操作系统的API,通过操作系统内核创建新线程的PCB,并且要把执行的指令交给当前PBC。当PCB被调度到CPU上执行的时候,也就执行到了线程run方法中的代码了。
java有一个主线程main,通过t.start();主线程调用t.start创建出一个新的线程,新的线程调用run
run方法执行完毕,新的这个线程自然销毁。

import static java.lang.Thread.sleep;

class MyTread extends Thread{
    @Override
    public void run() {
        while(true) {
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("hello run");
        }
    }
}
public class TreadDemo {
    public static void main(String[] args) {
        Thread t = new MyTread();
        t.start();
        while(true){
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("hello main");
        }
    }
}

在这里插入图片描述
start和run之间的区别:
start是真正创建了一个线程,线程是独立的执行流。run只是描述了线程要做什么。
可以使用jdk自带的工具jconsole查看当前的java进程中的所有线程。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
PCB不是“简称”是一个数据结构,体现的是进程/线程是如何实现,如何被描述的。

在这里插入图片描述
堆栈跟踪(调用栈):描述了当前方法的调用关系。

创建线程的写法

1.继承Thread,重写run

在这里插入图片描述

2.实现Runnable接口

class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("hello thread");
    }
}
public class TreadDemo2 {
    public static void main(String[] args) {
        //描述了一个任务
        Runnable runnable = new MyRunnable();
        //把任务交给线程来执行
        Thread t = new Thread(runnable);
        t.start();
    }
}

在这里插入图片描述

3.使用匿名内部类,继承Thread

public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread t = new Thread() {
            public void run(){
                System.out.println("hello");
            }
        };
        t.start();
    }
}

在这里插入图片描述

4.使用匿名内部类,实现Runable

public class ThreadDemo4 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        });
        t.start();
    }
}

在这里插入图片描述

5.使用Lambda表达式

public class ThreadDemo5 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            System.out.println("hello");
        });
        t.start();
    }
}

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

Thread用法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target,String name)使用Runnable对象创建线程对象,并命名

在这里插入图片描述
t为代码里的变量名,mythread是系统里的线程名。

Thread的几个常见属性

属性获取方法
IDgetId()
名称(构造方法中起的)getName()
线程状态getState()
优先级(可以设置,但没用)getPriority()
是否后台(守护线程)线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

说明:

isDaemon()
前台线程:会阻止进程结束,前台线程的工作没做完,进程无法结束。 后台线程:不会阻止进程结束,后台线程工作未完成,进程可以结束。
代码中手动创建的线程,默认都是前台的。main线程默认也是前台的。 可以手动使用setDaemon设置成后台线程。
在这里插入图片描述

isAlive()
在这里插入图片描述

在这里插入图片描述
如果t的run还没跑,isAlive就是false
如果t的run正在跑,isAlive就是true
如果t的run跑完了,isAlive就是false

在这里插入图片描述
运行结果:
在这里插入图片描述
两个线程在微观上可能是并行的(两个核心),也可能是并发的(一个核心)宏观上无法感知,我们所看到的就是随机执行。系统是**抢占式调度。**多线程代码,需要考虑很多种可能性!

启动一个线程-start()

之前我们已经看到了如何通过覆写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。

覆写 run 方法是提供给线程要做的事情的指令清单
线程对象可以认为是把 李四、王五叫过来了
而调用 start() 方法,就是喊一声:”行动起来!“,线程才真正独立去执行了。

调用start方法,才是真的在操作系统的底层创建出一个线程。

线程终止

线程终止,不是让线程立即停止。而是通知线程要停止了。但是至于线程是否停止了,取决于代码的具体写法。

举个列子: 我在打游戏,麻麻突然告诉我家里的酱油没了。让我去打酱油~我有以下几个选择:

  1. 停止游戏,立即去
  2. 打完这把,再去
  3. 假装没听见,不去

类比如下:

  1. 使用标志位来控制线程是否要停止。
    在这里插入图片描述
  2. 使用Thread自带的标志位来进行判断
public class ThreadDemo8 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while (!Thread.currentThread().isInterrupted()){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        t.interrupt();
    }
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
interrupt会做两件事:

  1. 把线程内部的标志位(boolean)设置成true。
  2. 如果线程正在进行sleep,就会触发异常。把sleep唤醒~

sleep被唤醒后,会将刚才设置的标志位,再设置回false。

t线程忽略终止请求:
在这里插入图片描述

线程t立即响应终止请求:
在这里插入图片描述
稍后进行终止:
在这里插入图片描述

等待一个线程join()

线程是一个随机调度的过程。等待线程就是在控制两个线程的结束顺序。

在这里插入图片描述
本身执行完start之后,t和main线程就并发执行。main继续往下执行,t也会继续往下执行。但是此时使用了join,就导致main线程发生阻塞(block)。一直阻塞到t线程执行结束,main线程才会从join中恢复,才能继续往下执行。
t线程肯定比main线程先结束。
在这里插入图片描述
在这里插入图片描述

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos)同理,但可以更高精度

获取当前线程的引用

方法说明
public static Thread currentThread();返回当前线程对象的引用

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

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

相关文章

【ARMv8/v9 MMU 页表配置 01 】

文章目录 1.1 MMU1.1.1 虚拟地址位宽配置1.1.2 页面大小(grandule size)配置1.1.3 AArch64 页表项描述符格式1.1.4 内存属性配置 1.1 MMU 1.1.1 虚拟地址位宽配置 64 位虚拟地址中,并不是所有位都用上,除了高 16 位用于区分内核空间和用户空间的虚拟地…

SpringCloud:ElasticSearch之数据聚合

聚合(aggregations) 可以让我们极其方便的实现对数据的统计、分析、运算。例如: 什么品牌的手机最受欢迎?这些手机的平均价格、最高价格、最低价格?这些手机每月的销售情况如何? 实现这些统计功能的比数据…

数组题目总结 -- 双指针

目录① 快慢指针:一. 删除有序数组中的重复项1. 思路和代码I. 博主的做法:II. 东哥的做法:2. 总结二. 删除排序链表中的重复元素(扩展)1. 思路和代码I. 博主的做法:II. 东哥的做法:2. 总结三. 移…

硬件语言Verilog HDL牛客刷题day08 综合部分

1. Johnson Counter 1.题目: 请用Verilog实现4位约翰逊计数器(扭环形计数器),计数器的循环状态如下。 电路的接口如下图所示 2.解题思路 2.1 一个简单的状态机的配置。 2.2 注意 起始状态 是 0000 就行 3.解题代码 timescale …

动态内存管理——C语言【进阶】(下)

作者简介: 辭七七,目前大一,正在学习C/C,Java,Python等 作者主页: 七七的个人主页 文章收录专栏:进阶C语言,本专栏主要讲解数据存储,进阶指针,动态内存管理&a…

前端面试题 - 计算机网络与浏览器相关

系列文章目录 vue常见面试题总结 htmlcss 面试题总结附答案 初级前端面试题总结(html, css, js, ajax,http) js基础面试题整理(包含ES5,ES6) 文章目录 系列文章目录一、网络协议相关1. 从浏览器地址栏输入url到显示页面的步骤…

蓝桥杯欲伸手CTF?有多远爬多远

注意:网络安全类比赛 或者说 CTF 参赛不会需要任何费用 只有国赛/省赛有可能会收取一定运维费用 其他比赛都不会收费 望周知。 先来看个特离谱的事情 早上起床看到几位师傅的朋友圈一脸懵,再仔细一看,好嘛。。。。。。 先看看探姬的回复 接下…

人工智能大模型多场景应用原理解析

前言 在上篇文章《人工智能大模型之ChatGPT原理解析》中分享了一些大模型之ChatGPT的核心原理后,收到大量读者的反馈,诸如:在了解了核心原理后想进一步了解未来的发展趋势(比如生成式人工智能和元宇宙能擦出什么样的火花?),大模型…

伪命题之MYSQL分库分表

看到使用分库分表来解决性能问题的时候心里总是不能太理解。 如果同事发生大量请求的时候,损害性能的是硬盘的随机读。那么分库分表也没有对性能的瓶颈进行“分治”啊。 应该的做法是使用一块新的硬盘来创建分库。但是基本的文章都没有提到这点。而且基本上也不会有…

价值导向型研发管理数字化建设方案——易趋亮相CCTI中国研发管理峰会

4月15日-16日,由光环国际举办的2023 CCTI中国研发管理峰会在北京中关村国家自主创新示范区会议中心成功举办。 (现场签到处) 此次峰会邀请了20余位来自腾讯、华为、网易、阿里云等知名企业的研发管理领域专家,带来最新前沿知识和内…

【网络安全知识体系】外网渗透测试 | 14个小实验

写在前面:视频地址 成功上岸360!0基础网络安全 入行 到 入yu、漏洞挖掘-外网渗透测试流程目录 一、导读: 二、汇总: 三、知识导图 四、面试常见问题 五、渗透测试流程 1、简述: 2、寻找测试目标 3、信息收集 …

webgl-简单动画

html <!DOCTYPE html> <head> <style> *{ margin: 0px; padding: 0px; } </style> </head> <body> <canvas id webgl> 您的浏览器不支持HTML5,请更换浏览器 </canvas> <script src"./main.js"></script&g…

程序员必用的6个代码对比神器附下载地址

一、WinMerge WinMerge是一款基于Windows系统下的文件比较和合并工具&#xff0c;使用它可以非常方便地比较多个文档内容&#xff0c;适合程序员或经常需要撰写文稿的朋友使用。WinMerge会将两个文件内容做对比&#xff0c;并在相异之处以高亮度的方式显示&#xff0c;让使用者…

认识网络随机丢包

考虑一根漏水的管子&#xff0c;希望出水口接到和不漏的管子等量的水&#xff0c;要么靠时间&#xff0c;反复将漏掉的水重新注入&#xff0c;直到漏掉的水可忽略&#xff1a; ​ 要么靠空间&#xff0c;在漏的地方将管子加粗&#xff0c;一次性注入更多的水&#xff1a; 不…

什么是HTTP? HTTP和HTTPS的区别?

目录 1、什么是HTTP? 2、HTTP的特点 ① 支持客户/服务器模式 ② 简单快速 ③ 灵活 ④ 无连接 ⑤ 无状态 3、HTTPS 4、HTTP和HTTPS的区别 ① 安全性不同 ② 默认端口不同 ③ 响应速度和资源消耗不同 ④ 网站申请流程不同 ⑤ 对搜索排名的提升不同 1、什么是…

基于OpenCV的硬币面值识别

本项目通过Python与Opencv结合数字图像处理技术对&#xff11;元、&#xff15;角、&#xff11;角三种硬币进行识别。首先通过Canny算子对图像进行边缘检测&#xff0c;然后进一步调用定义的函数去除边缘检测后图像中的孤立点&#xff0c;对处理后的图像进行Hough变换检测圆曲…

涨点技巧: 谷歌强势推出优化器Lion,引入到Yolov5/Yolov7,内存更小、效率更高,秒杀Adam(W)

1.Lion优化器介绍 论文:https://arxiv.org/abs/2302.06675 代码:automl/lion at master google/automl GitHub 1.1 简单、内存高效、运行速度更快 1)与 AdamW 和各种自适应优化器需要同时保存一阶和二阶矩相比,Lion 只需要动量,将额外的内存占用减半; 2)由于 Lion…

港联证券|机器视觉迎重磅利好,5只概念股获两路资金加持

现在我国机器视觉在工业场景中的全体渗透率仍旧在10%以下&#xff0c;比照工业场景巨大的体量而言&#xff0c;机器视觉职业仍有较大开展空间。 近来*ST中潜收到广东证监局行政监管措施决定书。依据深交所的相关要求&#xff0c;公司应当在2023年1月31日前发表公司股票或许被停…

windows安装opencv-python(opencv-python源码安装)

因为我要开启opencv的GStreamer功能&#xff0c;这是和ffmpeg相类似的对视频流操作的一个功能&#xff0c;默认没有开启&#xff0c;需要手动编译。 安装方式链接: opencv-install-with-GStreamer 核心内容如下: git clone --recursive https://github.com/skvark/opencv-py…

数据结构_第十二关:二叉树的基础OJ题练习

目录 1.单值二叉树 2.相同的树 3.另一棵树的子树 4.反转二叉树 5.对称二叉树 6.二叉树的结构及遍历 扩展&#xff1a;如何判断是不是完全二叉树、二叉树的销毁 1&#xff09;判断是不是完全二叉树 2&#xff09;二叉树的销毁 1.单值二叉树 OJ题链接https://leetcode.…