多线程基础知识点

1. 进程

一个正在执行中的程序就是一个进程,系统会为这个进程发配独立的【内存资源】。进程是程序的一次执行过程,它有自己独立的生命周期,它会在启动程序时产生,运行程序时存在,关闭程序时消亡。

例如:正在运行的 QQ、IDE、浏览器就是进程。

2. 线程

线程是由进程创建的,是进程的一个实体,是具体干活的人,一个进程可能有多个线程。线程不独立分配内存,而是共享进程的内存资源,线程可以共享 CPU 的计算资源。

一个进程的线程就不能修改另一个线程的数据,隔离性更好,安全性更好。

3. 并发和并行

大部分操作系统的任务调度是采用时间片轮转的抢占式调度方式,也就是说一个任务执行一小段时间后强制暂停去执行下一个任务,每个任务轮流执行。CPU 在不同的进程之间轮换,进程又在不同的线程之间轮换,因此线程是 CPU 执行和调度的最小单元。

任务执行的一小段时间叫做时间片,任务正在执行时的状态叫运行状态,任务执行一段时间后强制暂停去执行下一个任务,被暂停的任务就处于就绪状态,等待下一个属于它的时间片的到来。这样每个任务都能得到执行,由于 CPU 的执行效率非常高,时间片非常短,在各个任务之间快速地切换,给人的感觉就是多个任务在“同时进行”,这也就是我们所说的并发。

并发是两个队列交替使用一台咖啡机。

并行是两个队列同时使用两台咖啡机。

4. Java 中常见线程方式

4.1 继承 Thread 类

步骤:

  • 定义类继承 Thread;
  • 重写 Thread 类中的 run 方法;
  • 调用线程的 start 方法:
public class Test01 {public static void main(String[] args) {System.out.println(1);new MyThread01().start();System.out.println(3);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(4);} 
}class MyThread01 extends Thread{@Overridepublic void run() {System.out.println(2);}
}

4.2 实现 Runnable 接口

步骤:

  • 创建类实现 Runnable 接口
  • 使用 Thread 为这个任务分配线程
  • 调用线程的 start 方法
public class Test02 {public static void main(String[] args) {System.out.println(1);//注意,这里 new 的是 Threadnew Thread(new MyRun()).start();System.out.println(3);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(4);}}class MyRun implements Runnable{public void run() {System.out.println(2);}
}

4.3 实现 Callable 接口

步骤:

  • 创建类实现 Callable 接口
  • 通过 Callable 接口实现类创建 FutureTask
  • 使用 Thread 为这个 FutureTask 分配线程
  • 调用线程的 start 方法
public class Test03 {public static void main(String[] args) throws ExecutionException, InterruptedException {System.out.println(2);FutureTask<Integer> futureTask = new  FutureTask<>(new MyCallable());System.out.println(3);new Thread(futureTask).start();System.out.println(4);int result = futureTask.get();System.out.println(5);System.out.println(result);System.out.println(6);}}class MyCallable implements Callable<Integer> {public Integer call() throws Exception {Thread.sleep(2000);return 1;}
}

futureTask.get(); 是一个阻塞的方法,意思就是,这个方法会一直等,主线程会一直等待,这个线程执行完成之后并有了返回值,才会继续执行。

5. 守护线程

Java 提供两种类型的线程: 用户线程和守护线程。

守护线程旨在为用户线程提供服务,并且仅在用户线程运行时才需要。

守护线程对于后台支持任务非常有用,例如垃圾收集,释放未使用对象的内存以及从缓存中删除不需要的数据。大多数 JVM 线程都是守护线程。

要将线程设置为守护线程,我们需要做的就是调用 Thread 的 setDaemon() 方法。

NewThread t = new NewThread();
t.setDaemon(true);
t.start();

6. 线程的生命周期

生命周期可以通俗地理解为“从出生到死亡”的整个过程。线程的生命周期包括从创建到销毁的整个过程。

线程的状态:

  • NEW - 初始状态,一个新创建的线程,还没开始执行。
  • RUNNABLE - 可执行的状态,要么是在执行,要么是一切就绪等待执行,例如等待分配 CPU 时间。
  • WAITING - 等待状态,等待其他的线程去执行特定的动作,没有时间限制。
  • TIMED_WAITING - 限时等待状态,等待其他的线程去执行特定的动作,这个是在一个指定的时间范围内。
  • BLOCKED - 阻塞状态,等待锁,以便进入同步块儿。
  • TERMINATED - 终止状态,线程执行结束。

图片描述

7. 线程常用方法

currentThread() :该方法是 Thread 类中的类方法,可以用类名调用,方法返回当前正在使用 CPU 资源的线程。

sleep() :使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。

join() :让当前线程邀请调用方法的那个线程优先执行,在被邀请的线程执行结束之前当前线程一直处于阻塞状态,不再继续执行。

yield() :让当前线程直接放弃时间片返回就绪状态。

wait() :当前线程放弃监视器并进入睡眠状态,直到其他进入同一个监视器的线程调用 notify 为止。

notify() :唤醒同一监听器中调用 wait 的某一个线程。

notifyAll() :唤醒同一监听器中所有等待的线程。

8. 线程安全

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

9. 线程同步

线程同步:关键字 synchronized。

使用同步监视器来判断当前代码是否有线程在执行。线程开始执行同步代码块之前,必须先获得对同步监视器的锁。

任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行结束后,该线程自然会释放对同步监视器对象的锁。

Java 程序运行可以使用任何对象来作为同步监视器对象。

synchronized 有三种方式来加锁,分别是:

  1. 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
  2. 静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
  3. 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

10. synchronized 原理

Synchronized 底层是通过两个方法来完成同步:

  • monitorenter
  • monitorexit

10.1 monitorenter

每个对象有一个监视器锁(monitor)。当 monitor 被占用时就会处于锁定状态,线程执行 monitorenter 指令时尝试获取 monitor 的所有权,过程如下:

  1. 如果 monitor 的进入数为 0,则该线程进入 monitor,然后将进入数设置为 1,该线程即为 monitor 的所有者。

  2. 如果线程已经占有该 monitor,只是重新进入,则进入 monitor 的进入数加 1.

  3. 如果其他线程已经占用了 monitor,则该线程进入阻塞状态,直到 monitor 的进入数为 0,再重新尝试获取 monitor 的所有权。

10.2 monitorexit

执行 monitorexit 的线程必须是 objectref 所对应的 monitor 的所有者。

指令执行时,monitor 的进入数减 1,如果减 1 后进入数为 0,那线程退出 monitor,不再是这个 monitor 的所有者。其他被这个 monitor 阻塞的线程可以尝试去获取这个 monitor 的所有权。

11. 死锁

死锁问题:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结束。结构如下图所示:

死锁示例代码:

public class MyThread01 implements Runnable{static Object garlic = new Object();static Object vinegar = new Object();int flag = 0;@Overridepublic void run() {if (flag == 0) {synchronized (vinegar) {try {Thread.sleep(500);} catch (InterruptedException e) {return;}System.out.println("我张三正在吃醋,李四你给我点蒜瓣");synchronized (garlic) {System.out.println("我张三终于吃上蒜了,hiahia~");}}}if (flag == 1) {synchronized (garlic) {try {Thread.sleep(500);} catch (InterruptedException e) {}System.out.println("俺李四正在吃蒜,张三你给我点醋");synchronized (vinegar) {System.out.println("我李四终于吃上醋了,hiahia~");}}}}public static void main(String[] args) {MyThread01 ZhangSan = new MyThread01();MyThread01 LiSi = new MyThread01();ZhangSan.flag = 0;LiSi.flag = 1;Thread t1 = new Thread(ZhangSan);Thread t2 = new Thread(LiSi);t1.start();t2.start();}
}

程序运行结果:

图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

57.6K star!一个免费开源的 API 开发生态系统

&#xff01;&#xff01;&#xff01;文末有链接&#xff01;&#xff01;&#xff01; 小伙伴们&#xff0c;你们有没有遇到这样的问题呢&#xff1f;当你作为前端开发者和后端开发者一起协同工作时&#xff0c;联调接口成了必须要做的工作。 而为了验证接口的稳定性和安全…

【Linux操作系统】探秘Linux奥秘:shell 编程的解密与实战

&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《操作系统实验室》&#x1f516;诗赋清音&#xff1a;柳垂轻絮拂人衣&#xff0c;心随风舞梦飞。 山川湖海皆可涉&#xff0c;勇者征途逐星辉。 目录 &#x1fa90;1 初识Linux OS &…

技术概述:ARMv8体系结构

John Goodacre, Director Program Management ARM Processor Division, November 2011 背景&#xff1a;ARM体系结构 从ARM精简指令集体系结构提出到现在已经有20多年了&#xff1b;ARMv7系列处理器是在ARMv4基础上设计的&#xff0c;随着ARMv7系列处理器大量应用&#xff0…

在VM下使用Composer完成快照方式的软件制作

Composer允许您构建软件、应用程序、偏好设置文件或是文档的安装包&#xff0c;安装包可以部署到远程电脑或是作为镜像流程的一部分。构建软件包的第一步就是创建包源&#xff0c;根据要打包的软件&#xff0c;Composer允许您监视软件的安装和使用驱动器上已存在的文件来创建包…

Transformer架构和对照代码详解

1、英文架构图 下面图中展示了Transformer的英文架构&#xff0c;英文架构中的模块名称和具体代码一一对应&#xff0c;方便大家对照代码、理解和使用。 2、编码器 2.1 编码器介绍 从宏观⻆度来看&#xff0c;Transformer的编码器是由多个相同的层叠加⽽ 成的&#xff0c;每个…

LLM Agent零微调范式 ReAct Self Ask

前三章我们分别介绍了思维链的使用&#xff0c;原理和在小模型上的使用。这一章我们正式进入应用层面&#xff0c;聊聊如何把思维链和工具使用结合得到人工智能代理。 要回答我们为什么需要AI代理&#xff1f;代理可以解决哪些问题&#xff1f;可以有以下两个视角 首先是我们…

TSINGSEE青犀智能分析网关V4在智慧园区车辆违停检测场景中的应用

一、背景与需求 园区作为企业办公、生产制造的重要场所&#xff0c;主要道路车辆违停等违规行为会对园区的安全造成隐患&#xff0c;并且在上下班高峰期内&#xff0c;由于发现不及时&#xff0c;车辆违停行为会造成出入口拥堵现象&#xff0c;这也成为园区管理的棘手问题。为了…

二叉搜索树/二叉排序树(代码实现)(c++)

BSTree 二叉排序树概念代码部分 BSTree框架查找操作插入操作删除操作**默认成员函数完整代码 BSTree性能分析 二叉排序树概念 二叉搜索树又称二叉排序树&#xff08;BSTree, Binary Search Tree&#xff09;&#xff0c;它或者是一颗空树&#xff0c;或者是具有以下性质的二叉…

DNs服务学习笔记

DNS&#xff1a;域名系统&#xff08;英文&#xff1a;Domain Name System)是一个域名系统&#xff0c;是万维网上作为域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使用户更方便的访问互联网&#xff0c;而不用去记住能够被机器直接读取的IP数串。类似于生活中的11…

计算机毕业设计-----ssm+mysql实现的JavaWeb酒店管理系统

项目介绍 本项目为基于ssmmysql实现的JavaWeb酒店管理系统; 主要功能包括&#xff1a; 管理员登录,收入统计,客房管理,商品管理,客房预订,住宿登记,财务统计,旅客管理,接待对象管理等功能。 环境需要 1.运行环境&#xff1a;最好是java jdk 1.8&#xff0c;我们在这个平台上…

Efficient Classification of Very Large Images with Tiny Objects(CVPR2022补1)

文章目录 Two-stage Hierarchical Attention SamplingsummaryOne-stageTwo-Stage内存需求 Efficient Contrastive Learning with Attention Sampling Two-stage Hierarchical Attention Sampling summary 从一个大图像中按照指定的低分辨率比例和位置提取出一个小图块 一阶段…

C#编程-实现继承

C#允许您通过扩展现有类的功能以创建新类来实现继承。 从基类创建派生类 使用以下语法在C#中创建派生类: class <derived_class>:<base_class>{...}确定继承的层次结构 要确定继承层次结构,必须检查派生类与基类之间的关系种类。确保派生类是一种基类。 请考虑以…