线程安全和synchronized关键字

news2024/10/6 18:35:49

一,线程安全的引入

1.示例

多线程在多进程的基础上更好解决了并发问题,但由于一个进程内的多个线程是资源共享的,就会出现多个线程在并发执行的时候造成内存中数据的混乱。

举一个例子:

class Counter {
    public int count;

    public void add() {
        count++;
    }
}

public class ThreadDemo1 {
    public static void main(String[] args) {
        Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.add();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.add();
            }
        });
        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("count = " + counter.count);
    }
}

 

这里定义两个线程实例对象t1,t2(执行的任务分别是循环调用50000次add操作),add方法的作用是对成员变量count进行++,我们设想两个线程并发执行,结果输出的count值应该为100000,但是并不是,这就是一个典型的线程安全问题!!!

2.原因

首先我们需要明确add操作主要干了什么?

可以看出 add操作分为了三个指令,没进行一次++CPU就会执行三条指令(其中这三条指令是串行的)

load :把内存中的值加载到CPU的寄存器上

add  :在CPU的寄存器上进行++操作

save: 把计算之后的值再存到内存当中

那为什么会在进行add操作的时候会出现输出结果小于100000呢?

我们知道两个线程在并发编程的时候是抢占式执行的(谁先抢到CPU资源谁就会被优先调度,此时另一个线程就会阻塞),此时两个线程中的add操作锁所对应的指令就会出现很多种情况,就会造成计算结果出错。

 

 这里举出了两个例子,两个线程的所对应的三条指令顺序都不一样(是因为线程1,2抢占式执行当某一个线程执行其中一条指令时,另一个线程被调度时会优先执行另一个线程的指令,此时之前的线程就会被阻塞),第一种情况add了两次正常输出2,而第二种情况只被add了一次输出1;

二,线程安全

1.概念

什么是线程安全:线程安全确切的定义十分复杂,所以我们一般认为,如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的;

上述例子若改成单线程:

class Counter2 {
    public int count;

    public void add() {
        count++;
    }
}

public class Test {
    public static void main(String[] args) {
        Counter2 counter2 = new Counter2();
        for (int i = 0; i < 50000; i++) {
            counter2.add();
        }
        for (int i = 0; i < 50000; i++) {
            counter2.add();
        }
        System.out.println("count = " + counter2.count);
    }
}

 此时输出正确,所以我们可以认为上述输出结果不是预期的那种多线程代码是线程不安全的。

2.原因

1.抢占式执行,随机调度(根本原因,由操作系统内核决定,无法改变)

2.因为单个进程下的多个线程是资源共享的,所以多个线程修改同一个变量时会线程不安全

   多个线程修改不同的变量  没事

   一个线程修改同一个变量  没事

   多个线程读取同一个变量  没事

3.原子性

   如果修改操作是原子性的,就不会有线程安全问题

   如果修改操作是非原子性的就很大概率出现线程安全问题(上述示例的add操作就是非原子性         的,一个add操作分成了三个指令去执行)

   所以我们去避免线程安全的主要手段就是将非原子性的操作变成原子性---->加锁

4.内存可见性问题

    当一个线程在读取数据,一个线程在修改数据时就会出现线程安全问题(也就是常说的脏读问        题)

5.指令重排序(本质上是代码出现了bug)

三,synchronized关键字

针对线程安全问题,我们往往常用的手段就是把非原子性操作变成原子性操作,此时就需要用到synchronized关键字(该关键字的作用就是对对象加锁)

针对上述的代码进行修改:

class Counter {
    public int count;

    synchronized public void add() {
        count++;
    }
}

public class ThreadDemo1 {
    public static void main(String[] args) {
        Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.add();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.add();
            }
        });
        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("count = " + counter.count);
    }
}

 

此时输出结果为我们预期的100000;

1.synchronized 的原理

在多线程环境下,当一个线程在被调度时(拿上述例子解释:当线程t1调用add方法进行conut++操作时,因为线程是抢占式执行的,此时线程t2想要调用add方法时发现线程t1正在执行add方法,线程t2就会发生线程阻塞,等待线程t1完全执行完时线程t2才可以进行add操作),另一个线程就会阻塞等待,直到当前线程执行完毕,另一个线程才可以执行。

当有一个滑稽老铁在上厕所时(厕所相当于对象,滑稽老铁相当于线程),此时门就会上锁(这个锁的作用就相当于synchronized的作用),其余的滑稽老铁必须等待当前的滑稽老铁上完厕所,他们才可以使用这个厕所(相当于此时其他线程是阻塞等待的)。

2.synchronized 的特性

1.互斥

指的是当一个线程执行到synchronized对象中时,另一个对象执行到这个对象时就会阻塞等待;

进入 synchronized 修饰的代码块, 相当于 加锁

退出 synchronized 修饰的代码块 , 相当于 解锁
2.可重入
synchronized 同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题;

 

 当同一个线程对一个对象多次加锁时,并不会出现问题时就称该锁为可重入,否则就称该锁不可重入(此时会造成死锁的问题)

3.synchronized的使用案例

synchronized可以修饰方法(包括实例方法和静态方法)也可以修饰代码块

修饰实例方法:锁的是synchronized对象;

 修饰静态方法:锁的是Counter类对象;

 修饰代码块:锁的是当前对象;

其中的this可以改成任意对象;

4.synchronized总结 

如果两个线程针对同一个对象进行加锁,就会出现锁竞争/锁冲突,一个线程能够获取到锁(先到先得)另一个线程阻塞等待,等待到上一个线程解锁,它才能获取锁成功,否则就不会;

如果两个线程针对不同对象加锁,此时不会发生锁竞争/锁冲突,这俩线程都能获取到各自的锁,不会有阻塞等待了;

两个线程,一个线程加锁,一个线程不加锁这个时候就不会有锁竞争。

四,Java 标准库中的线程安全类                                            

线程不安全的集合类线程安全的集合类
ArrayList
Vector ( 不推荐使用 )
LinkedList
HashTable ( 不推荐使用 )
HashMap
ConcurrentHashMap
TreeMapStringBuffer
HashSet
TreeSet
StringBuilder

虽然有的集合类是加锁了,但是在使用时并不是建议无脑使用加锁的集合类,因为加锁也需要很多的时间开销(根据情况进行选择)

还有的虽然没有加锁,但是不涉及 "修改", 仍然是线程安全的:String

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

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

相关文章

hypervision理解的记录

目录 一、hypervision介绍 Type 1 Hypervisor Type 2 Hypervisor 二、QNX hypervision是TYPE1的虚拟机 三、QNX hypervision架构 1、VMM (虚拟机管理器) 2、virtual-net 3、qnx官网 network 九、其他 一、hypervision介绍 首先&#xff0c;hypervision分为Type1和Type2…

SpringBoot添加外部jar包及打包(亲测有效) - 第452篇

历史文章&#xff08;文章累计450&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 深入Feign源码吃透Spring扩展点「扩展点…

亿级异构任务调度框架设计与实践

背景 阿里云日志服务作为云原生可观测与分析平台。提供了一站式的数据采集、加工、查询分析、可视化、告警、消费与投递等功能。全面提升用户的研发、运维、运营、安全场景的数字化能力。 日志服务平台作为可观测性平台提供了数据导入、数据加工、聚集加工、告警、智能巡检、…

“200万天价床垫”引发的思考:普通床垫越卖越贵是推测还是事实

定制床垫价格高达200万元&#xff1f;近段时间&#xff0c;一场娱乐圈的闹剧让大家把目光转向了床垫市场。在天价床垫的话题下&#xff0c;除了大部分猜测床垫品牌的讨论以外&#xff0c;也有不少人认为指出了“社会现状”&#xff1a;健康品质化的消费追求正在让市面上的床垫价…

Python_数据容器_字典

一、字典&#xff08;映射&#xff09;的定义 生活中的字典&#xff1a; 【字】&#xff1a;【含义】 可以按【字】找出对应的【含义】 Python中的字典&#xff1a; key : value 可以按照[key]找出对应的[value] 1、Python字典使用场景&#xff1a; 通过使用字典&#…

uni-app 之 web-view 与h5 通讯

官网文档&#xff1a;https://uniapp.dcloud.net.cn/component/web-view.html#getenv web-view 是一个 web 浏览器组件&#xff0c;可以用来承载网页的容器&#xff0c;会自动铺满整个页面&#xff08;nvue 使用需要手动指定宽高&#xff09;。 各小程序平台&#xff0c;web-v…

图像下采样再上采样维度不匹配

图像在下采样后再上采样&#xff0c;维度会发生不匹配&#xff0c;假设一幅图像的维度为(b,c,h,w)&#xff0c;那么当h和w是偶数的时候&#xff0c;下采样和上采样是匹配的&#xff0c;当且仅当他是偶数的时候才匹配&#xff0c;然而图像的h和w往往不一定是偶数。当然有许多种方…

【Shell 脚本速成】06、Shell 数组详解

目录 一、数组介绍 二、数组定义 三、数组赋值方式 四、数组取值 案例演示 五、关联数组 5.1 定义管理数组 5.2 关联数组赋值 5.3 管理数组取值 5.4 综合案例 有这样一个现实问题&#xff1a;一个班级学员信息系统&#xff0c;要求存储学员ID、NAME、SCORE、AGE、GE…

关于订单功能的处理和分析

这两天看了一下RABC的权限管理处理&#xff0c;梳理了一下订单功能的表创建&#xff0c;界面&#xff0c;功能分析。 目录 RABC RBAC0模型 那么对于RABC模型我们怎么创建数据库表&#xff1f; 订单模块的梳理 RABC RABC说的是在用户和权限之间多一个角色&#xff0c;用户与…

软件测试基础

⭐️前言⭐️ &#x1f349;博客主页&#xff1a; &#x1f341;【如风暖阳】&#x1f341; &#x1f349;精品Java专栏【JavaSE】、【备战蓝桥】、【JavaEE初阶】、【MySQL】、【数据结构】 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&…

Spring Cloud OpenFeign - - - >拦截器

源码地址&#xff1a;https://download.csdn.net/download/weixin_42950079/87209379 SpringMVC拦截器 和 OpenFeign拦截器 的区别 初学者很容易将 Spring MVC 拦截器 和 Spring Cloud OpenFeign 拦截器搞混&#xff0c;误以为OpenFeign拦截器就是SpringMVC拦截器&#xff1a; …

虹科分享 | 麦氏比浊仪在药敏试验中的应用

细菌是重要的病原微生物&#xff0c;人类针对不同的病原菌研发了各类抗菌药&#xff0c;这些药物对细菌性疾病的治疗与控制起到了关键作用。然而随着新型致病菌的不断出现&#xff0c;加上细菌在药物使用过程中逐渐产生了耐药性&#xff0c;抗菌药的防治效果越来越差。病原菌对…

Python 中的类与继承

类的定义以及实例的建立 Python中&#xff0c;类通过 class 关键字定义。 例如最简单的一个类定义可以为&#xff1a; class Person(object):pass Python 的编程习惯&#xff0c;类名以大写字母开头&#xff0c;紧接着是(object)&#xff0c;表示该类是从哪个类继承下来的。…

解决单文件组件里的跨域请求数据问题(使用vue单文件组件请求数据必会遇到的问题!!!)

为什么要解决跨域问题&#xff1a; 因为浏览器有限制&#xff0c;只有同域名同端口号下的数据才能拿来用&#xff1b;那如果想拿到不同域名不同端口号下的数据就不行了&#xff1b; 在单文件组件中如何去解决跨域问题&#xff1a; 因为服务器没有跨域限制&#xff0c;只有浏览…

PyTorch(四)Torchvision 与 Transforms

文章目录Log一、Torchvision1. CIFAR10① 介绍② 使用2. 与 Transforms 结合使用总结Log 2022.11.28接着开启新的一章2022.11.29继续学习 一、Torchvision 视频教程中 Torchvision v0.9.0文档Torchvision 官方文档Torchvision Datasets API 文档 1. CIFAR10 ① 介绍 CIFAR…

HBuilder X实现banner轮播图

第一步还是去仔细阅读官方文档&#xff0c;找到组件下面的内置组件里面的swiper&#xff0c;如图所示 官方是提供了一个用来制作轮播图的滑块视图容器&#xff1a;swiper 一般来说&#xff0c;轮播图的图片地址是由后端返回给前端遍历显示在页面上的&#xff0c;所以基本的结构…

世界杯的“中国元素”昂扬大国担当,点面科技全新推出的多模态多功能移动终端踏上卡塔尔征途!

本次世界杯中国足球队没有参加&#xff0c;但是在世界杯的赛场上到处都有中国元素。 中国承建的卢赛尔体育场 卡塔尔世界杯主体育场——卢赛尔球场由中国铁建以总承包身份承建&#xff0c;引发了全球瞩目。在本届世界杯赛事中&#xff0c;该场馆将承担包括决赛在内的10场比赛&…

idea手动创建webapp(在main文件夹下)

SSM自学笔记 文章目录一、Maven使用正常情况首先不使用骨架创建好Maven项目然后选择Project Structure...选择要创建webapp的模块修改路径二、Maven不正常工作时一、Maven使用正常情况 首先不使用骨架创建好Maven项目 然后选择Project Structure… 选择要创建webapp的模块 选好…

前置微小信号放大器在光声技术的血管识别研究中的应用

实验名称&#xff1a;前置微小信号放大器在光声技术的血管识别研究中的应用 研究方向&#xff1a;生物识别技术 测试目的&#xff1a; 利用MATLAB对光声血管进行识别&#xff1a;1、对光声血管图库的图像进行预处理包括归一化、二值化、平滑、细化和毛刺修剪得到细化图像&#…

0109 蓝桥杯真题12

/* * 观察下面的现象,某个数字的立方&#xff0c;按位累加仍然等于自身。 * 1^3 1 * 8^3 512 5128 * 17^3 4913 491317 ... *请你计算包括1,8,17在内&#xff0c;符合这个性质的正整数一共有多少个&#xff1f; *填写该数字&#xff0c;不要填写任何多余的内…