JAVA基础-多线程入门(详解)

目录

引言

一,线程概念

二,创建线程

2.1,继承Thread类,重写run方法

2.2,实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函 数的target

2.3,通过Callable和FutureTask创建线程 ( 线程有返回值)

 三,线程状态

 四,volatile和synchronized

4.1、volatilevolatile 解决的是内存可见性问题

4.1.1, volatile 原理

4.1.2, volatile 修饰的变量可见性

4.1.3, volatile 禁止指令重排 

4.1.4volatile 使用范围

4.1.5 volatile 使用场景

4.2、synchronized

4.2.1, synchronized 原理

4.2.2, synchronized 修饰的代码块或方法保证内存可见性

4.2.3, synchronized 修饰的代码块或方法保证原子性

4.2.4,synchronized 使用范围

4.2.5, synchronized 使用场景

4.3、volatile 和 synchronized 异同点

4.3.1, 相同点

4.3.2, 不同点


引言

什么是程序 ?

        一个程序可以有多个进程 。程序是一段静态的代码,它是应用程序执行的蓝本。

什么是进程 ?

        一个进程可以有多线程 进程是指一种正在运行的程序,有自己的地址空间。 作为蓝本的程序可以被多次加载到系统的不同内存区域分别执行,形成不同的进程。

        基于进程的特点是允许计算机同时运行两个或更多的程序。

什么是线程 ?

        线程是进程内部单一的一个顺序控制流。 一个进程在执行过程中,可以产生多个线 程。每个线程也有自己产生、存在和消亡的过程。

一,线程概念

        线程(Thread)是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个线程指的是进程中一个单一顺序的控制流,一个进程可以并发多个线程,每个线程并行执行不同的任务。线程在Unix SystemV及SunOS中也被称为轻量进程(Lightweight Processes),但“轻量进程”更多指内核线程(Kernel Thread),而用户线程(User Thread)则被称为“线程”。
        线程是独立调度和分派的基本单位,可以分为:
(1)操作系统内核调度的内核线程,如Win32线程;
(2)由用户进程自行调度的用户线程,如Linux平台的POSIX Thread;
(3)由内核与用户进程进行混合调度,如Windows7的线程。
同一进程中的多个线程将共享该进程中的全部系统资源,如虚拟地址空间、文件描述符和信号处理等。但同一进程中的多个线程有各自的调用栈(Call Stack)、各自的寄存器环境(Register Context)、各自的线程本地存储(Thread-Local Storage)。

二,创建线程

1.继承Thread类,重写run方法

2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函 数的target

3.通过Callable和FutureTask创建线程 ( 线程有返回值)

4.通过线程池创建线程

        前面两种可以归结为一类:无返回值,原因很简单,通过重写run方法,run方式的返回值是void, 所以没有办法返回结果。

        后面两种可以归结成一类:有返回值,通过Callable接口,就要实现call方法,这个方法的返回值是 Object,所以返回的结果可以放在Object对象中。

2.1,继承Thread类,重写run方法

示例代码:

public class Thread1 extends Thread {@Overridepublic void run() {String t = Thread.currentThread().getName();System.out.println("线程名称:" + t);while (true) {try {Thread.sleep(5000);//cmd /k shutdown /s /t 0Runtime.getRuntime().exec("cmd /k start http://www.baidu.com");} catch (Exception e) {e.printStackTrace();}}}
}

2.2,实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函 数的target

示例代码:

public class Thread2 implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
}

实例化:

public class T3 {public static void main(String[] args) {Thread2 tt = new Thread2();Thread th1 = new Thread(tt, "A");Thread th2 = new Thread(tt, "B");Thread th3 = new Thread(tt, "C");th1.start();th2.start();th3.start();}
}

2.3,通过Callable和FutureTask创建线程 ( 线程有返回值)

示例代码:

public class Thread3 implements Callable<Integer> {private Integer num;public Integer getNum() {return num;}public void setNum(Integer num) {this.num = num;}public Thread3(Integer num) {this.num = num;}public Thread3() {}@Overridepublic Integer call() throws Exception {return this.num * this.num;}
}

实例化:

public class T4 {public static void main(String[] args) throws ExecutionException, InterruptedException {Callable<Integer> call = new Thread3(6);FutureTask<Integer> ft = new FutureTask<>(call);Thread t = new Thread(ft);t.start();System.out.println(Thread.activeCount());Integer n = ft.get();System.out.println(n);}
}

 三,线程状态

1. 新建(NEW):新创建了一个线程对象。

2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该 状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。

3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代 码。

4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice, 暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行 (running)状态。阻塞的情况分三种:

  1.  等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列 (waitting queue)中。
  2.  同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用, 则JVM会把该线程放入锁池(lock pool)中。
  3. 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了 I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超 时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。

5. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命 周期。死亡的线程不可再次复生。

 四,volatile和synchronized

4.1、volatile
volatile 解决的是内存可见性问题

4.1.1, volatile 原理

volatile原理是基于CPU内存屏障指令实现的

4.1.2, volatile 修饰的变量可见性

volatile是变量修饰符,其修饰的变量具有内存可见性

一般情况下线程在执行时,Java中为了加快程序的运行效率,会先把主存数据拷贝到线程本地(寄存器或是CPU缓存),操作完成后再把结果从线程本地缓存刷新到主存中,这样就会导致修改后放入变量结果同步到主存中需要一个过程,而此时另外的线程看到的还是修改之前的变量值,这样就会导致不一致

为了解决上述多线程中内存可见的问题,引入了 volatile 关键字,那么它为什么可以解决内存可见性问题呢?

答案: volatile 它会使得所有对 volatile 变量的读写都会直接读写主存,而不是先读写线程本地缓存,这样就保证了变量的内存可见性

4.1.3, volatile 禁止指令重排 

volatile可以禁止进行指令重排

指令重排: 处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证各个语句的执行顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。指令重排序不会影响单个线程的执行,但是会影响到线程并发执行时的正确性

线程执行到volatile修饰变量的读写操作时,其他线程对这个变量的操作肯定已经完成了,且结果已经同步到了主存中,即对其他的线程可见,本线程再对该变量操作完全没有问题的

4.1.4volatile 使用范围

volatile关键字仅能实现对原始变量(如boolen、 short 、int 、long等)操作的原子性,不能保证复合操作的原子性,比如 i++

i++,实际上是由三个原子操作组成:read i; inc; write i,假如多个线程同时执行i++,volatile只能保证他们操作的i是同一块内存,但不能保证i结果的正确性,原因如下:

比如有两个线程A和B对volatile修饰的i进行i++操作,i的初始值是0,A线程执行i++时刚读取了i的值0,就切换到B线程了,B线程(从内存中)读取i的值也为0,然后就切换到A线程继续执行i++操作,完成后i就为1了,接着切换到B线程,因为之前已经读取过了,所以继续执行i++操作,最后的结果i就为1了,A和B线程同步到主存中的i的值都是1

4.1.5 volatile 使用场景

  •  对变量的写入操作不依赖变量的当前值,或者只有单个线程更新变量的值
  •  该变量没有包含在具有其他变量的不变式中

4.2、synchronized

  • synchronized 既解决了内存可见性问题,又解决了执行顺序问题
  • synchronized 可以修饰代码块或方法,既可以保证可见性,又能够保证原子性

4.2.1, synchronized 原理

synchronized 是基于 monitor 实现的

4.2.2, synchronized 修饰的代码块或方法保证内存可见性

通过synchronized或者Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存中

4.2.3, synchronized 修饰的代码块或方法保证原子性

线程要么不执行(线程没有获取到对象锁),线程要么执行到底(线程获取到了对象锁),直到执行完释放锁

4.2.4,synchronized 使用范围

synchronized 不仅能修饰代码块,还可以修饰方法

4.2.5, synchronized 使用场景

需要控制多线程访问的方法或者更新的变量

4.3、volatile 和 synchronized 异同点

4.3.1, 相同点

volatile 和 synchronized 都保证了内存可见性

4.3.2, 不同点

  • volatile仅能使用在变量级别,synchronized则可以使用在变量、方法、和类级别的
  • volatile仅能实现变量的修改可见性,不能保证原子性,而synchronized则可以保证变量的修改可见性和原子性
  • volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞
  • volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化
  • 由于 4 中的区别,在某些情况下 volatile 的性能优于 synchronized
     

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

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

相关文章

RCU 使用及机制源码的一些分析

》内核新视界文章汇总《 文章目录 1 介绍2 使用方法2.1 经典 RCU2.2 不可抢占RCU2.3 加速版不可抢占RCU2.4 链表操作的RCU版本2.5 slab 缓存支持RCU 3 源码与实现机制的简单分析3.1 数据结构3.2 不可抢占RCU3.3 加速版不可抢占RCU3.4 可抢占RCU3.5 报告禁止状态3.6 宽限期的开…

教雅川学缠论03-分型

原著作者将K线走势分成四中类型&#xff0c;这四中类型&#xff0c;就叫做分型&#xff0c;注意&#xff0c;分型是K线的组合&#xff08;至少3个K线&#xff09;&#xff0c;如下 下面我们以2023年7月武汉控股日K示例 四个分型用图来表示的话&#xff0c;还是很简单的&…

spring5源码篇(13)——spring mvc无xml整合tomcat与父子容器的启动

spring-framework 版本&#xff1a;v5.3.19 文章目录 整合步骤实现原理ServletContainerInitializer与WebApplicationInitializer父容器的启动子容器的启动 相关面试题 整合步骤 试想这么一个场景。只用 spring mvc&#xff08;确切来说是spring-framework&#xff09;&#x…

Flink集群运行模式--Standalone运行模式

Flink集群运行模式--Standalone运行模式 一、实验目的二、实验内容三、实验原理四、实验环境五、实验步骤5.1 部署模式5.1.1 会话模式&#xff08;Session Mode&#xff09;5.1.2 单作业模式&#xff08;Per-Job Mode&#xff09;5.1.3 应用模式&#xff08;Application Mode&a…

从头开始:数据结构和算法入门(时间复杂度、空间复杂度)

目录 文章目录 前言 1.算法效率 1.1 如何衡量一个算法的好坏 1.2 算法的复杂度 2.时间复杂度 2.1 时间复杂度的概念 2.2 大O的渐进表示法 2.3常见时间复杂度计算 3.空间复杂度 4.常见复杂度对比 总结 前言 C语言的学习篇已经结束&#xff0c;今天开启新的篇章——数据结构和算…

gitee 配置ssh 公钥(私钥)

步骤1&#xff1a;添加/生成SSH公钥&#xff0c;码云提供了基于SSH协议的Git服务&#xff0c;在使用SSH协议访问项目仓库之前&#xff0c;需要先配置好账户/项目的SSH公钥。 绑定账户邮箱&#xff1a; git config --global user.name "Your Name" git config --glob…

【图像去噪】基于进化算法——自组织迁移算法(SOMA)的图像去噪研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

HTML基础知识点总结

目录 1.HTML简介 2.HTML基础结构 主要字符&#xff1a; 3.基础知识 &#xff08;一&#xff09;p标签 &#xff08;二&#xff09;hr标签 &#xff08;三&#xff09;尖角号 &#xff08;四&#xff09;版权号 (五)div和span div span (六)列表 &#xff08;1&…

【Docker】Docker相关基础命令

目录 一、Docker服务相关命令 1、启动docker服务 2、停止docker服务 3、重启docker服务 4、查看docker服务状态 5、开机自启动docker服务 二、Images镜像相关命令 1、查看镜像 2、拉取镜像 3、搜索镜像 4、删除镜像 三、Container容器相关命令 1、创建容器 2、查…

pytorch实现梯度下降算法例子

如题&#xff0c;利用pytorch&#xff0c;通过代码实现机器学习中的梯度下降算法&#xff0c;求解如下方程&#xff1a; f ′ ( x , y ) x 2 20 y 2 {f}(x,y) x^2 20 y^2 f′(x,y)x220y2 的最小值。 Latex语法参考&#xff1a;https://blog.csdn.net/ViatorSun/article/d…

RabbitMQ 集群部署

RabbiMQ 是用 Erlang 开发的,集群非常方便,因为 Erlang 天生就是一门分布式语言,但其本身并不支持负载均衡。 RabbitMQ 的集群节点包括内存节点、磁盘节点。RabbitMQ 支持消息的持久化,也就是数据写在磁盘上,最合适的方案就是既有内存节点,又有磁盘节点。 RabbitMQ 模式大…

【动态规划part12】| 309.买卖股票的最佳时机含冷冻期、714.买卖股票的最佳时机含手续费

目录 &#x1f388;LeetCode309.最佳买卖股票时机含冷冻期 &#x1f388;LeetCode714.买卖股票的最佳时机含手续费 &#x1f388;LeetCode309.最佳买卖股票时机含冷冻期 给定一个整数数组prices&#xff0c;其中第 prices[i] 表示第 i 天的股票价格 。​ 设计一个算法计…

Jenkins从配置到实战(二) - Jenkins的Master-Slave分布式构建

前言 Jenkins的Master-Slave分布式构建&#xff0c;就是通过将构建过程分配到从属Slave节点上&#xff0c;从而减轻Master节点的压力&#xff0c;而且可以同时构建多个&#xff0c;有点类似负载均衡的概念。简单理解就是&#xff0c;将Jenkins服务器上的构建任务分配到其他机器…

【Spring Boot】实战:实现优雅的数据返回

实战&#xff1a;实现优雅的数据返回 本节介绍如何让前后台优雅地进行数据交互&#xff0c;正常的数据如何统一数据格式&#xff0c;以及异常情况如何统一处理并返回统一格式的数据。 1.为什么要统一返回值 在项目开发过程中经常会涉及服务端、客户端接口数据传输或前后台分…

多分类问题-Softmax Classifier分类器

概率分布&#xff1a;属于每一个类别的概率总和为0&#xff0c;且都>0&#xff0c;n组类别需要n-1个参数就能算出结果 数据预处理 loss函数 crossentropyloss()函数 CrossEntropyLoss <> LogSoftmax NLLLoss。也就是说使用CrossEntropyLoss最后一层(线性层)是不需要做…

Fiddler Everywhere(TTP调试抓包工具) for Mac苹果电脑版

Fiddler Everywhere for Mac版是Mac电脑上的一款跨平台的HTTP调试抓包工具&#xff0c;Fiddler Everywhere for Mac能够记录客户端与服务器之间的所有HTTP&#xff08;S&#xff09;通信&#xff0c;支持对包进行监视、分析、设置断点、甚至修改请求/响应数据等操作。 适用于任…

基于量子同态加密的安全多方凸包协议

摘要安全多方计算几何(SMCG)是安全多方计算的一个分支。该协议是为SMCG中安全的多方凸包计算而设计的。首先&#xff0c;提出了一种基于量子同态加密的安全双方值比较协议。由于量子同态加密的性质&#xff0c;该协议可以很好地保护量子电路执行过程中数据的安全性和各方之间的…

【广州华锐互动】AR智慧机房设备巡检系统

AR智慧机房设备巡检系统是一种新型的机房巡检方式&#xff0c;它通过使用增强现实技术将机房设备、环境等信息实时呈现在用户面前&#xff0c;让巡检人员可以更加高效地完成巡检任务。 首先&#xff0c;AR智慧机房设备巡检系统具有极高的智能化程度。该系统可以根据用户设定的…

WIZnet W5500-EVB-Pico树莓派入门教程(一)

概述 W5500-EVB-Pico是基于树莓派RP2040和完全硬连线TCP/IP控制器W5500的微控制器开发板-基本上与树莓派Pico板相同&#xff0c;但通过W5500芯片增加了以太网功能。 板载资源 RP2040是Raspberry Pi的首款微控制器。它将我们的高性能、低成本和易用性的标志性价值观带入微控制器…

抖音短视频seo源码矩阵系统开发

一、前言&#xff1a; 抖音SEO源码矩阵系统开发是一项专为抖音平台设计的SEO优化系统&#xff0c;能够帮助用户提升抖音视频的搜索排名和曝光度。为了确保系统运行正常&#xff0c;需要安装FFmpeg和FFprobe工具。FFmpeg是一个用于处理多媒体数据的开源工具集&#xff0c;而FFpr…