java线程多线程并发

news2024/11/27 21:49:36

文章目录

  • 对java线程的认识
    • wait()和sleep()区别?
    • wait,notify为什么要放在同步代码块中?
  • 多线程
    • **什么时候使用多线程**:
    • **多线程的优缺点**:
    • **线程安全问题**:
    • 并行和并发
    • **线程同步**
    • synchronize和Lock
    • 线程和进程有什么区别?
    • JMM内存模型
    • JMM的八大原子操作
    • 并发编程的三大核心
      • 产生三大问题的原因以及解决方案
      • volatile底层原理

对java线程的认识

线程、进程、程序之间的关系:

程序:是一段用某种语言编写的具有某种功能的指令集合。

进程:正在运行的程序,Windows系统中,资源调度的最小单位。

线程:进程的进一步细化,是一个进程内部的最小执行单元,是操作系统进行任务调度的最小 单元,属于进程。

一个进程可以有多个线程,一个线程只能属于一个进程。

java创建线程的方式有三种:

①继承Thread类,重写run方法;②实现Runnable接口,重写run方法;③实现Callable接口,重写call方法;

使用实现Callable接口方式,在调用线程时需要借助FuTure类获取实现类对象的返回值,然后用传入Thread对象实例中。如下:

//创建实现Callable接口类的对象
CallableDemo callableDemo = new CallableDemo();
//借助FutureTask获取返回值
FutureTask<Integer> futureTask = new FutureTask(callableDemo);
//利用Thread开启线程
Thread t = new Thread(futureTask);
//开启线程
t.start();
//futureTask.get()获取call方法的返回值
System.out.println(futureTask.get());

Thread中会有一些线程相关的方法:

start()开启线程;
setName(String name)设置线程的名称;
getName()获取线程名称;
setPriority(int newPriority) 设置线程的优先级;
getPriority()获得线程的优先级;
join()使线程处于等待状态(抛异常);
sleep(long millis)使线程进入休眠状态(单位为:毫秒,抛异常);
currentThread()返回当前正在执行线程的对象的引用。

线程有新建、就绪、执行、阻塞、死亡五种状态

java中的线程可以分为守护线程可用户线程。当有用户线程运行时守护线程就会一直运行,当所有的用户线程都终止时守护线程才会终止。

java是支持多线程的。

wait()和sleep()区别?

wait()是Object类中的方法,sleep()是Thread中的方法;

两个方法都可以使线程进入等待状态,但是wait后需要使用notify或者notifyAll唤醒,sleep在休眠结束后自动苏醒;

wait后释放掉锁(不释放掉锁,其他线程如何获取锁,又如何调用notify或者notifyAll方法将其唤醒呢),sleep只释放CPU资源,不释放锁;

wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep没有限制。

wait,notify为什么要放在同步代码块中?

wait,notify是用于线程之间的协调通信,wait表示让线程进入阻塞状态,notify表示让阻塞的线程被唤醒。wait和notify必然是成对出现的,如果一个线程wait后必然需要另一个线程通过notify进行唤醒,从而达到线程之间的通信,在多线程中要实现多线程之间的通信除了使用管道流外只能使用共享变量的方法实现,也就是说线程t1访问和修改共享变量s,线程t2获得修改后的共享变量s,从而完成多线程的通信,但是多线程本身具有并行执行的特性,也就是说同一个时刻多个线程同时执行,这种状态下线程t2在访问共享变量s时必须知道s是否已经被修改,否则需要等待。同时t1在修改后还需要把处于等待的线程t2唤醒,所以在这种状态下实现线程通信必须由一个静态条件去控制线程什么时候等待,什么时候被唤醒。而synchronize同步关键字就可以实现这个互斥的条件,也就是使用共享变量来实现多个线程通信的场景里,参与通信的线程必须要获得共享锁资源才能有资格进行修改共享变量,修改完成后释放锁,其他线程就可以再次竞争同一个共享锁资源,然后访问修改后的共享变量。从而完成线程之间的通信,synchronized同步锁可以实现线程之间的互斥从而实现条件等待和条件唤醒,为了规范wait和notify的使用,jdk强制要求将wait和notify写在同步代码块中,否则运行时会报出IllegalMonitorException异常。wait和notify非常适合实现生产者和消费者的模型。

多线程

多线程:就是一个程序包含多个执行单元,一个程序内同一时间可以多个线程执行多个不同的任务。

什么时候使用多线程

当有多个任务需要执行时,当需要一些后台程序时,当程序需要一些等待任务时。

多线程的优缺点

多线程的优点:提高CPU的利用率,提高程序的响应速率,改善程序结构,将复杂的程序结构分给不同的线程,独立运行。

多线程的缺点:线程过多时会影响性能,线程越多需要的存储空间也越多,线程之间对共享资源的访问会相互影响,必须解决资源争夺问题。

线程安全问题

线程安全就是,在多线程当中,有多个线程同时访问并操作同一个共享资源。这样会导致数据错乱。例如从银行卡里取钱,可以线下去ATM机中取现金,也可以用微信或者支付宝线上提现,线程安全问题就是,同时使用线上、线下两种方式,银行卡的金额只减少一次。

并行和并发

在单核CPU中所有的线程时串行,但是操作系统中有个组件叫做任务调度器,会将CPU的时间片分给不同的线程,而CPU在线程之间切换的非常快,给人的感觉就好像是线程在同时执行。总结就是微观串行,宏观并行。一般将CPU轮流执行线程的做法叫做并发。并行是相对于多核CPU而言的,多个线程进入不同的CPU执行。举个例子:一个人轮流吃三个馒头(并发),和三个人吃三个馒头(并行)。

线程同步

为了解决多个线程同时对共享资源操作,可能会引起数据混乱的问题,引入线程同步机制。

线程同步:当一个线程调用某种功能时,为保证数据的一致性,其他线程不能在调用此功能。

同步机制就是要求方法需要加锁,确保同一时间只有一个线程操作共享资源。

同步锁:同步锁可以是任意对象,但是必须唯一,确保多线程获取的是同一个锁对象。

synchronize和Lock

synchronizedLock
关键字接口
不需要手动释放锁需要手动释放锁
不能中断可以中断

线程通信:多个线程之间相互牵制、相互调用,相互作用。

Object提供的wite()方法:当前线程处于阻塞状态,并释放同步监视器。

notify()方法唤醒其他线程,当有多个线程时优先唤醒优先级高的。

notifyAll()方法唤醒所有阻塞线程。

线程和进程有什么区别?

一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。

JMM内存模型

JMM(内存模型):是java规范的一种工作模式。将内存分为主内存和工作内存,变量存储在主内存中,线程在操作变量时,需要将变量从主内存中复制到工作内存中,操作完成后又写回到主内存中。主内存的读写速度慢,工作内存的读写速度快。

这样做的好处:提高效率;缺点:多个线程同时修改了同一共享变量,会出现数据不准确的问题。

JMM的八大原子操作

read(读取):从主内存读取数据

load(载入):将从主内存读取到的数据写入到工作内存中

use(使用):从工作内存中读取数据用来计算

assign(赋值):将计算好的值重新赋值到工作内存中

store(存储):将工作内存的数据写入到主内存中

write(写入):将store过去的变量赋值给主内存中的变量

lock(锁定):将主内存变量加锁,表示为线程独占状态

unlock(解锁):将主内存变量解锁,解说后其他线程可以锁定该变量

在这里插入图片描述

并发编程的三大核心

可见性、有序性、原子性。

产生三大问题的原因以及解决方案

可见性对应就有不可见问题,因为JMM内存模型,一个线程在工作内存中修改共享变量其他线程时不知道的,也就是对其他线程时不可见的,这样就会容易出现数据不准确。

有序性对应的无序性是因为,java在编译的时候为了性能的优化,会将一些指令顺序重排,例如执行读指令的时候在等待返回的时候会先执行下一条指令。指令重拍会遵循两个原则:as-if-serial和happens-before原则。

as-if-serial原则:不管怎么重排序,单线程下程序执行的结果不能改变。编译器、runtime和处理器都必须遵循。

happens-before原则:判断数据是否存在竞争、线程是否安全的依据。原则内容(8个):

  1. 程序顺序原则:按照代码顺序执行。
  2. 锁规则:解锁操作发生在同一个锁的加锁之前。
  3. volatile规则:volatile变量的写,先发生与读,这点保证了volatile变量的可见性。也就是说,线程在操作变量时,都强迫从主内存中读取该变量的值,而当该变量发生变化时,又强迫立即将最新的数据刷新到主内存中,而其他线程总能够看到该变量的最新值。
  4. 线程启动规则:线程的start()方法优先于它的每一个动作,
  5. 传递原则
  6. 线程终止原则
  7. 线程中断原则
  8. 对象终结原则

volatile可以保证可见性和有序性。

原子性指的是“不可分割”的意思,这个指令要么执行,要么不执行不可中断,高级语言的一条语句可能并不是原子指令,例如:count++操作就不是一个原子操作,它分为三步:①、从内存中读取count;②、执行count+1操作;③、将count+1的结果写回内存。如果不是原子指令执行中途是可以中断的(也就是线程切换)可能会出现问题:线程1读取到了count=0,此时线程切换,线程2也读取到count=0,线程2继续执行并将结果count=1写回到主内存,此时继续执行线程1,线程1也将结果count=1写回到主内存中,此时进行了两次count++可是主内存的count还是=1,出现错误。

在这里插入图片描述

解决非原子就是要求同一时间内只能有一个线程对共享资源操作,同一时间只允许一个线程操作称为互斥。所以要达到原子性可以同过加锁来实现。

volatile底层原理

volatile关键字可以保证并发编程的可见性和有序性。

实现可见性:缓存一致性协议(MEIS):要求线程的工作内存在修改完一个变量后立即将该变量写回到主内存,所有的线程对总线程存在嗅探机制,一但嗅探到自己工作内存中的某个变量的值被改变了,就会将自己工作内存的该变量失效。进而重新从主内存总读取修改后的变量。底层主要是通过汇编lock前缀指令。

lock指令的作用:①会将当前处理器缓存行的数据立即写回到主内存;②写回到主内存的操作会引起其他线程里缓存了该内存地址的数据失效。

保证有序性:volatile修饰变量。

内存屏障机制:内存屏障是一条指令,可以对编译器和处理器的指令重排进行一些限制,比如内存屏障指令可以禁止它之后的指令被提取到它的前面。

单例模式

public class Window {
    private static volatile   Window  window=null;

    private Window(){
    }

    public static  Window getWindow(){
        if(window==null){
            synchronized(Window.class){
                if(window == null){
                    window  = new Window();
                }
            }
        }
        return window;
    }
}
/*
jvm底层new一个对象的过程是:
			1. 类加载检查:虚拟机遇到一条new指令时,首先会检查这个指令的参数是否能在常量池中定位到一个类的符号应用,并检查这个符号引用代表的类是否已经被加载,解析和初始化过。如果没有必须经过相应的过程。
			2. 分配内存:虚拟机将为新生的对象分配内存,也就是对象内存大小将在加载完成后就已经确定了,为对象分配内存的任务等同于把一块确定大小的内存从堆中划分出来。
			3. 初始化零值:此时为对象赋上默认值,并不是真正程序要赋予的值。
			4. 设置对象头:初始化零值之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例,如何找到对应的类的元数据信息,对象的哈希码,对象的GC年龄等信息。这些信息都在对象的对象头中存储。HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(header)、实例数据(Instance Data)和对齐填充(Padding)。对象头又分2部分:一部分用于存储运行时数据,如哈希吗、GC年龄、锁状态等;另一部分是类型指针,如对象指向它的类元数据的指针。
			5. 执行init方法:执行init方法时才真正的为对象赋予程序员想要赋予的初始值和执行构造方法。执行完init方法才算是一个对象真正构造完成。
			6. 设置方法区中的变量指向堆中刚创建的实例对象
		
	所以new一个对象并不是一个原子操作。并且这些指令可能会重排序
	当由于指令重排使得第六步优先于第五步,而恰巧又在第六步执行完之后切换到其他线程中,其他线程会判断对象已经初始化,直接返回,但是返回的对象只是一个半成品(对象半初始号问题),如果禁止指令重排,那就不会出现这个问题。volatile在此处的防止指令重排就会起到作用。
*/

在这里插入图片描述

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

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

相关文章

MATLAB应用

目录 网站 智能图像色彩缩减和量化 网站 https://yarpiz.com/ 智能图像色彩缩减和量化 使用智能聚类方法&#xff1a;&#xff08;a&#xff09;k均值算法&#xff0c;&#xff08;b&#xff09;模糊c均值聚类&#xff08;FCM&#xff09;和&#xff08;c&#xff09;自组织神…

Mysql—存储过程

简介 存储过程就是类似于MySQL触发器&#xff0c;经过事先编写好相应语句&#xff0c;通过编译后存储在数据库上。触发器不需要手动调用即可实现相应SQL功能。MySQL存储过程&#xff0c;需要自己去调用得到相应的结果。 语法 创建存储过程 CREATE DEFINER CURRENT_USER PR…

git---->团队开发神器,一篇文章直接掌握

git---->团队开发神器&#xff0c;一篇文章直接掌握 一 学习git的原因概念版本的概念1 版本控制软件的基础功能2 集中式版本控制软件3 分布式版本控制 二 git的安装三 GitHub Desktop的使用四 团队操作五 中央服务器--github从github上下载文件到本地仓库传输文件 六 国内中…

chatgpt赋能python:Python如何实现自增

Python如何实现自增 在Python编程中&#xff0c;自增是一种非常常用的操作&#xff0c;它可以让我们在循环、计数等场景中更加方便地进行操作。实际上&#xff0c;在Python中&#xff0c;实现自增非常简单&#xff0c;本文将介绍Python中常用的自增操作&#xff0c;并分享自增…

时间复杂度 空间复杂度

概览 时间复杂度与空间复杂度的作用是在衡量一个算法的优劣性&#xff0c;以及在二者之间进行权衡&#xff0c;寻找二者的平衡点。 时间复杂度是指执行算法所需时间的增长率&#xff0c;而空间复杂度则是指执行算法所需存储空间的增长率。 高时间复杂度的算法可能需要在短时间…

LayUI前框框架普及版

LayUI 一、课程目标 1. 【了解】LayUI框架 2. 【理解】LayUI基础使用 3. 【掌握】LayUI页面元素 4. 【掌握】LayUI内置模块二、LayUI基本使用 2.1 概念 layui&#xff08;谐音&#xff1a;类UI) 是一款采用自身模块规范编写的前端 UI 框架&#xff0…

Nginx+Tomcat 负载均衡、动静分离

目录 一、Nginx代理服务器概念 1.正向代理 2.反向代理 二、动静分离 三、负载均衡 四、Nginx七层代理实验 1.部署Nginx服务 2. 部署Tomcat服务 2.1在192.168.88.50 虚拟机上部署双实例 2.2在192.168.88.60 上部署Tomcat服务器3 3.动静分离配置 3.1Tomcat1 server 配…

RecyclerView的回收缓存均由内部类Recycler完成

1. RecyclerView的三级缓存 通常在RecyclerView中存在着四级缓存&#xff0c;从低到高分别为&#xff1a; 可直接重复使用的临时缓存&#xff08;mAttachedScrap/mChangedScrap&#xff09; mAttachedScrap中缓存的是屏幕中可见范围的ViewHoldermChangedScrap只能在预布局状态…

Material—— 常用材质节点

目录 Coordinates Absolute World Position Actor Position Object Position Utility SphereMask Coordinates 表示坐标类的节点&#xff1b; Absolute World Position 别名为WorldPosition&#xff0c;此节点输出当前像素在世界空间内的位置&#xff1b;常用于查找相机到…

作为过来人:有什么话想对当年高考前的自己说

目录 引言千人千面-有什么话想对当年高考前的自己说怀念高中&#xff0c;数学太难多考一分&#xff0c;人生就会不一样一定要勇敢&#xff0c;不止高考别把高考不当回事6的我没话说想到啥就去做别选**大学/专业强烈想出名的拖鞋哥英语全选C&#xff0c;理综要细心会的全做对当时…

Spring - 注解开发

文章目录 Spring的注解开发一、Bean 基本注解开发1.1 Component Bean的配置1.2 其他注解配置Bean1.3 Component 衍生注解 二、Bean依赖注入注解开发2.1 Value2.2 Autowired2.3 Qualifier2.4 Resource 三、非自定义注解开发3.1 无参非自定义注解开发3.2 有参非自定义注解开发 四…

Domino 14新内核

大家好&#xff0c;才是真的好。 还记得去年&#xff0c;我们不断跟进而放出的Notes/Domino产品路线图吗&#xff1f;是的&#xff0c;HCL正在按照产品路线图稳步推进&#xff0c;而很多人提出的idea&#xff0c;也逐步加入到产品中&#xff0c;等会我们也会聊到。 我最喜欢这…

MySQL安装-Linux版

MySQL-Linux版安装 1、准备一台Linux服务器 云服务器或者虚拟机都可以&#xff1b; Linux的版本为 CentOS7&#xff1b; 2、 下载Linux版MySQL安装包 下载地址 3、上传MySQL安装包 使用FinalShell软件上传即可&#xff01; 4、 创建目录,并解压 mkdir mysqltar -xvf my…

【Web服务器】Nginx网站服务

文章目录 一、Nginx 概述1.什么是 Nginx2.Nginx 的特点3.Nginx 应用场景 二、Nginx 服务基础1.编译安装 Nginx 服务1.1 布置环境1.2 安装依赖包1.3 创建运行用户、组1.4 编译安装 2.Nginx 的运行控制2.1 检查配置文件2.2 启动、停止 Nginx2.3 日志分割以及升级 Nginx 服务2.4 添…

调用腾讯API实现图片滤镜

目录 1. 作者介绍2. 图像滤波介绍3. 实验过程及结果&#xff08;附完整代码&#xff09;3.1 准备工作3.2 实验代码3.3 实验结果 1. 作者介绍 班梦威&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2022级研究生 研究方向&#xff1a;模式识别与人工智能 电子…

企业如何有效制定企业信息化发展规划?(附信息化模板)

如何有效制定企业信息化发展规划&#xff1f;企业信息化发展规划是一个宏大而又复杂的命题&#xff0c;这篇来掰开揉碎讲一下企业应该如何有效制定信息化发展规划。 这里不给大家灌鸡汤&#xff0c;也不给大家画大饼&#xff0c;就说些实在的。 如果你想找经验方法&#xff0…

Lambda表达式与函数式编程

文章目录 函数式编程——Stream流概述为什么学?函数式编程思想 Lambda表达式概述Lambda表达式的前身省略规则 Stream流概述案例数据准备创建流中间操作终结操作reduce归并注意事项 Optional概述创建对象安全消费值获取值安全获取值过滤数据转换 函数式接口常用的默认方法 方法…

APPSCAN扫描https协议的网站证书安装过程(Googel浏览器)

【1】首先打开我们的appscan,点击外部设备。 【2】点击记录代理配置 【3】弹出选项后&#xff0c;在记录代理下我们可以看到AppScan SSL证书&#xff0c;这点我们点击导出 【4】这里你选择一个合适的位置&#xff0c;点击保存 【5】保存后的文件是一个zip压缩包&#xff0c;…

GPT4和Claude100k测试使用

总述 程序员们通常使用大量代码&#xff0c;找到一个能够使用Claude100k和GPT4的&#xff0c;长代码优化有希望啦&#xff01; Liaobots&#xff1a;支持GPT4和Claude100k 不定期供应GPT4 32k&#xff0c;支持最多24000字符请求 大家有时候会觉得GPT4 8k不够用&#xff0c;…

TensorFlow入门知识

个人理解 TensorFlow是集齐了很多深度学习相关的算法的框架&#xff0c;你可以利用他搭建自己的神经网络模型。对于开发者来说&#xff0c;告诉TensorFlow一批特征数据和最终的答案数据&#xff0c;让其通过一个神经网络模型进行训练&#xff0c;最终输出模型。模型将应用于应…