【Java EE 初阶】如何保证线程安全二

news2024/11/20 8:39:38

目录

1.线程不安全是什么?

2.线程不安全的成因

 3.解决线程不安全之一Synchronized关键字(监视器锁)

1.Synchronized使用方法

2.锁对象是什么?

​3.锁对象的练习

4.Synchronized的特性

1.互斥性

2.刷新内存

3.可重入

5.总结

4.解决线程不安全之一Volatile关键字

1.首先我们来分析一下CPU对代码的执行

 2.将变量用volatile修饰

 3.缓存一致性协议

4.内存屏障

5.内存有序性,原子性

5.synchronized和volatile关键字的区别

 6.wait和notify关键字

7.wait()方法和sleep()方法的区别

8.Java中线程安全的类


线程安全一的博客在这里喔~

线程安全【一】

1.线程不安全是什么?

如果多线程环境下代码运行的结果与单线程环境下不一致时就会出现线程不安全的问题

2.线程不安全的成因

 3.解决线程不安全之一Synchronized关键字(监视器锁)

加锁之后把并行变为了串行,所以实现了内存可见,因为串行一定是内存可见的

1.Synchronized使用方法

2.锁对象是什么?

3.锁对象的练习

4.Synchronized的特性

1.互斥性

同一时间只有一个线程可以获取到锁,其他的线程都要阻塞等待

2.刷新内存

线程修改完变量的值后,会把新值写回主内存,但这是用并行转为串行的方式做到的

3.可重入

Synchronized是可以嵌套使用的,当前线程每重新获取一次锁,计数器就+1,每释放一次锁,计数器就-1,直至计数器为0,当前线程才会真正的释放锁 

 

5.总结

synchronized可以解决原子性,内存可见性,但是不能解决有序性的问题

并且synchronized并没有真正的通过线程之间的通信解决内存可见性,也没有解决有序性的问题

4.解决线程不安全之一Volatile关键字

1.首先我们来分析一下CPU对代码的执行

我们创建两个线程,一个线程不停的循环判断标识位,用第二个线程来修改标识位

代码展示:

public static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println("t1开始============");
            while (count == 0) {
                //to do
            }
            System.out.println("t1结束============");
        });
        t1.start();

        Thread t2 = new Thread(() -> {
            System.out.println("t2开始============");
            System.out.println("请输入一个数字:");
            Scanner sc = new Scanner(System.in);
            count = sc.nextInt();
            System.out.println("t2结束============");
        });
        TimeUnit.SECONDS.sleep(1);
        t2.start();
    }

结果我们发现,在第二个线程修改了标识位之后,第一个线程并没有停止运行

 2.将变量用volatile修饰

public static volatile int count = 0;

此时我们发现程序可以正常退出了,证明t1及时接收到了其他线程对变量修改后的值,解决了内存可见性的问题

不建议使用:

 3.缓存一致性协议

可以理解为一种通知机制,正是这个协议的存在解决了内存不可见的问题

 

 

4.内存屏障

对加了volatile的变量,加了以下内存屏障

当某个线程对变量发生写操作之后,就会通过缓存一致性协议来通知其他的线程缓存值失效

 

 所以volatile可以解决内存可见性

5.内存有序性,原子性

 内存有序性是指在保证程序正确的前提下,编译器,CPU对指令的优化过程

 volatile可以解决有序性问题

用volatile修饰的变量,就是要告诉编译器,不需要对这个变量所涉及到的操作做任何优化,从而实现有序性

volatile只用来修饰变量

volatile不具备原子性

通过synchronized和volatile关键字的搭配使用解决了原子性,内存可见性,有序性的问题

5.synchronized和volatile关键字的区别

 6.wait和notify关键字

wait()方法和nitify()方法都是Object类中的方法

wait 做的事情:

  • 使当前执行代码的线程进行等待. (把线程放到等待队列中)
  • 释放当前的锁
  • 满足一定条件时被唤醒, 重新尝试获取这个锁. 

wait 要搭配 synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常. 


wait 结束等待的条件:

  • 其他线程调用该对象的 notify 方法. 
  • wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间). 
  • 其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常.

notify 方法是唤醒等待的线程. 

  • 方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
  • 如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 "先来后到")
  • 在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁。

notify方法只是唤醒(CPU随即调度,并无先来后到)某一个等待线程. 使用notifyAll方法可以一次唤醒所有的等待线程(所有线程共同参与锁的竞争).

在使用过程中,wait和notify必须是同一个锁对象,且wait和notify被调用后,当前线程都会释放锁资源

join()是Thread类中定义的方法

7.wait()方法和sleep()方法的区别

 线程1调用sleep()方法后,进入休眠,线程进入阻塞等待的状态,此时并不会释放锁,也不会去执行其他线程,等到等待时间结束后,线程1会继续执行后面的代码块

8.Java中线程安全的类

  Java 标准库中很多都是线程不安全的. 这些类可能会涉及到多线程修改共享数据, 又没有任何加锁措施.  
  • ArrayList
  • LinkedList
  • HashMap
  • TreeMap
  • HashSet
  • TreeSet
  • StringBuilder
但是还有一些是线程安全的. 使用了一些锁机制来控制.
  • Vector (不推荐使用)
  • HashTable (不推荐使用)
  • ConcurrentHashMap
  • StringBuffer   

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

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

相关文章

事务及分布式事务解决方案

基础概念 1.1.事务 事务可以看做是一次大的活动,它由不同的小活动组成,这些活动要么全部成功,要么全部失败。 1.2.本地事务 在计算机系统中,更多的是通过关系型数据库来控制事务,利用数据库本身的事务特性来实现&a…

SAP 从入门到放弃系列之批次追溯功能

首先执行MB57,建立批次追溯关系,并存储在CHVW表。根据情况选择要追溯的期间,在过账日期范围内填写。 不勾选‘基于清单显示’,为ALV显示结果 勾选‘基于清单显示’,为清单显示结果 执行MB56,查询批次追溯 可以设置显示…

【.NET AI Books 前言】Azure OpenAI Service 入门

本书是为 .NET 开发者而写的,让 .NET 开发者能快速掌握 Azure OpenAI Service 的使用技巧。 ChatGPT 的到来意味着我们已经置身于 AI 引起的全新变革中,作为开发者你可能将面临几种改变: GPT 模型到来后,如何去架构好企业解决方案…

Dex-Net 2.0<论文>

题目:Deep Learning to Plan Robust Grasps with Synthetic Point Clouds and Analytic Grasp 引言 传统抓取方法的局限性 缺乏泛化能力需要大量计算资源和手工标注数据【前两种依赖物体形状、材料、质量等先验知识通常要对物体建模姿态评估运动学分析】只能处理…

【C语言】深入理解注释

文章目录 一. 预处理阶段对注释的处理二. 注释使用时的注意事项1. C风格的注释无法嵌套使用2. 基本注释注意事项3. 注释导致的二义性 四. 关于注释的一个使用建议 一. 预处理阶段对注释的处理 我们知道一个源文件要变成可执行程序的话,首先要经过预处理&#xff0c…

Vtk7.1.1+PCL1.12.0安装

错误可参考:Ubuntu20.04 编译 pcl1.8可能出现的问题 安装参考1:ubuntu20.04下安装pcl_ubuntu安装pcl_Yuannau_jk的博客-CSDN博客 安装参考2:Ubuntu20.04 安装pcl详细教程_ubuntu20.04安装pcl_LYiiiiiii的博客-CSDN博客 安装参考3&#xff1a…

涨知识!你不知道的中国手机号码的编码和划分规则

引言 在当今信息化的时代,移动电话号码已经成为人们日常生活中必不可少的联系方式。中国作为世界上拥有庞大人口数量的国家之一,移动电话号码的编码和划分显得尤为重要。 中国的移动电话号码分为三大运营商,每个运营商又有自己的号码段&…

Spring Security OAuth2.0(三)-----基于Redis存储和JDBC存储

问题 令牌往哪里存? 客户端信息入库 第三方应用优化 1.令牌往哪里存? 在我们配置授权码模式的时候,有两个东西当时存在了内存中: InMemoryAuthorizationCodeServices 这个表授权码存在内存中。InMemoryTokenStore 表示生成的令…

open3D

一、说明 对于点云 处理,这里介绍哦pen3d,该软件和opencv同样是interl公司的产品。 Open3D 是一个开源库,支持快速开发处理 3D 数据的软件。 Open3D 前端在 C 和 Python 中公开了一组精心挑选的数据结构和算法。后端经过高度优化,…

Spring Boot处理CORS跨域请求的三种方法

1 前言 Springboot跨域问题,是当前主流web开发人员都绕不开的难题。但我们首先要明确以下几点 跨域只存在于浏览器端,不存在于安卓/ios/Node.js/python/ java等其它环境跨域请求能发出去,服务端能收到请求并正常返回结果,只是结…

05-权限分配 尚筹网

权限控制 权限控制机制的本质就是“用钥匙开锁”。 在实现权限控制之前,这里先完成给Admin分配Role和给Role分配Auth的功能。 一、给Admin分配Role 目标 ​ 通过前端页面操作,将Admin与Role之间的关系保存到数据库 思路 ​ 给下面的按钮&#xff…

【ED合集】事件检测的文章

1 CorED: Incorporating Type-level and Instance-level Correlationsfor Fine-grained Event Detection 论文来源:SIGIR 2022(CCF A类会议) 论文链接:https://dl.acm.org/doi/pdf/10.1145/3477495.3531956 代码链接:GitHub - JiaweiSheng…

抖音小程序|基于天气API实现天气预报功能

文章目录 一、前言包含了功能UI展示 二、开发前的准备三、开发步骤1.app.js 配置2.pages/index.js 演示二维码源码在百度网盘下载 一、前言 参考老版iPhone自带的天气预报APP。目前只有一个界面UI, 后续会更新出更多功能; 包含了功能 - 实况预报 - 未来48小时 - 未来一周的天…

动态gif图片如何在线做?轻松实现图片在线生成gif

常见的jpg、png格式的静态图片想要变成gif格式的动态图片时,要怎么办呢?有没有什么简单实用的gif制作工具呢? 一、什么工具能够在线制作gif? GIF中文网作为一款专业的gif制作(https://www.gif.cn/)工具&a…

Golang - slice 内部实现原理解析

Golang - slice 内部实现原理解析 一.Go中的数组和slice的关系 1.数组 在几乎所有的计算机语言中,数组的实现都是一段连续的内存空间,Go语言数组的实现也是如此,但是Go语言中的数组和C语言中数组还是有所不同的 C语言数组变量是指向数组第…

鸿蒙Hi3861学习七-Huawei LiteOS(信号量)

一、简介 信号量(Semaphore)是一种实现任务间通信的机制,实现任务之间同步或临界资源的互斥访问。常用于协助一组相互竞争的任务来访问临界资源。 在多任务系统中,各任务之间需要同步或互斥实现临界资源的保护,信号量功…

阿里工作7年,肝到P8就靠这份学习笔记了,已助14个朋友拿到offer

​ 在阿里工作了7年,工作压力大,节奏快,但是从技术上确实得到了成长,尤其是当你维护与大促相关的系统的时候,熬到P8也费了不少心思。 技术的更新迭代越来越快,程序员或许是这个过程中最为挣扎的一波人。每…

第0章 学习之前的准备

突然想写点关于linux的东西,一是将自己几十年来零碎的知识作以串联,二是能为正在学习路上的新手作些指引。而恰好作者的孩子是一位初一的学生,我写的这些东西也正是我手把手教授他的,现在分享出来并且命名为《linux中学教程》&…

记一次SpringBoot应用性能调优过程

背景 使用SpringBoot、MyBatis-Plus开发一个接口转发的能,将第三方接口注册到平台中,由平台对外提供统一的地址,平台转发时记录接口的转发日志信息。开发完成后使用Jmeter进行性能测试,使用100个线程、持续压测180秒,…

Java中池化技术探讨

背景:在日常开发中,除了考虑IO操作、线程上下文切换、GC的影响性能外。还通过池化技术提高性能通过循环复用资源,降低资源创建和销毁带来的开销和损失,从而提高性能,例如对象池、内存池、线程池、连接池 一、对象池&a…