理解线程安全:保护你的代码免受并发问题困扰

2024-06-12 01:36

本文主要是介绍理解线程安全:保护你的代码免受并发问题困扰,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

前言

一、什么是线程安全?

二、为什么需要线程安全?

三、实现线程安全的方法

四、synchronized

使用 synchronized 关键字时,需要注意以下几点:

五、Demo讲解


前言

        在现代软件开发中,尤其是在多线程编程中,线程安全(Thread Safety)是一个至关重要但又复杂的话题。本文将从基础概念开始,逐步深入,帮助你理解什么是线程安全,以及如何在实际开发中实现线程安全。

一、什么是线程安全?

        线程安全是指多个线程可以同时访问和修改共享数据而不导致数据的不一致性或程序错误。简单来说,当一个对象或函数在多线程环境下能够正确地执行,并且不会引起任何未定义的行为时,它就是线程安全的。

二、为什么需要线程安全?

        多线程编程的主要目的是为了提高程序性能和响应速度。然而,当多个线程同时操作共享资源时,可能会出现竞争条件(Race Condition)、死锁(Deadlock)以及饥饿(Starvation)等问题。这些问题不仅会导致程序运行结果不正确,还可能引发崩溃和难以调试的错误。

三、实现线程安全的方法

  1. 互斥同步:使用锁(如 synchronized、ReentrantLock 等)来保护共享数据,在同一时刻只允许一个线程访问共享数据,其他线程需要等待锁释放后才能访问。

  2. 原子操作:使用原子操作来确保对共享数据的操作是不可分割的,不会被中断,常见的原子操作包括 atomic 包下的原子类,以及 volatile 关键字修饰的变量。

  3. 无锁并发编程:通过使用无锁的数据结构和算法来实现线程安全,例如 CAS(Compare and Swap)操作,乐观锁机制等。

  4. 线程封闭:将共享数据限制在单个线程内部,避免多个线程之间直接访问共享数据,从而避免竞态条件。

  5. 不可变对象:通过创建不可变对象来避免多线程并发修改共享状态,从而避免线程安全问题。

四、synchronized

        当一个线程访问一个被 synchronized 修饰的方法或代码块时,会自动获取该方法或代码块对应的锁,其他线程必须等待该线程释放锁后才能获取锁并进入临界区。这种方式可以有效避免多个线程同时访问共享数据时发生竞态条件和其他线程安全问题。

使用 synchronized 关键字时,需要注意以下几点:

  1. 修饰方法:在修饰方法时,锁对象默认为当前对象(this),不同的线程需要获取的是同一个对象的锁。

  2. 修饰代码块:在修饰代码块时,需要指定锁对象,一般可以使用 this、类名.class 等作为锁对象。需要注意的是,锁对象必须是同一个对象才能保证线程同步。

  3. 可重入性:在同一个线程中,如果一个方法已经获取了锁,那么在调用另一个被 synchronized 修饰的方法时,该线程仍然能够获取到同一个锁,即 synchronized 具有可重入性。

  4. 锁的释放:在 synchronized 块执行完毕或者抛出异常时,锁会自动释放。如果在 synchronized 块中使用了 wait() 方法,则锁也会被释放,等待其他线程唤醒后再次获取锁。

五、Demo讲解

package com.ctb.demo;/*** 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个对象始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的* synchronized:可以在任意对象或方法上加锁,而加锁的这段代码称为"互斥区"或"临界区"* @author 彪**/
public class MyThread extends Thread{private int count = 5;public void run() {count--;System.out.println(this.currentThread().getName()+"count = "+count);}public static void main(String[] args) {/*** 	当多个线程访问MyThread的run方法时,以排队的方式进行处理(这里排队是按照CPU分配的先后顺序而定的)*   一个线程想要执行synchronized修饰的方法里的代码*   1.尝试获得锁*   2.如果拿到锁,执行synchronized代码内容:拿不到锁,这个线程就会不断尝试获得这把锁,直道拿到为止,*   	而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题)*/MyThread myThread = new MyThread();Thread t1 = new Thread(myThread,"t1");Thread t2 = new Thread(myThread,"t2");Thread t3 = new Thread(myThread,"t3");Thread t4 = new Thread(myThread,"t4");Thread t5 = new Thread(myThread,"t5");t1.start();t2.start();t3.start();t4.start();t5.start();}}

结果:并不是我们想要的,count--

package com.ctb.demo;/*** 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个对象始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的* synchronized:可以在任意对象或方法上加锁,而加锁的这段代码称为"互斥区"或"临界区"* @author 彪**/
public class MyThread extends Thread{private int count = 5;//synchronized加锁public synchronized void run() {count--;System.out.println(this.currentThread().getName()+"count = "+count);}public static void main(String[] args) {/*** 	当多个线程访问MyThread的run方法时,以排队的方式进行处理(这里排队是按照CPU分配的先后顺序而定的)*   一个线程想要执行synchronized修饰的方法里的代码*   1.尝试获得锁*   2.如果拿到锁,执行synchronized代码内容:拿不到锁,这个线程就会不断尝试获得这把锁,直道拿到为止,*   	而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题)*/MyThread myThread = new MyThread();Thread t1 = new Thread(myThread,"t1");Thread t2 = new Thread(myThread,"t2");Thread t3 = new Thread(myThread,"t3");Thread t4 = new Thread(myThread,"t4");Thread t5 = new Thread(myThread,"t5");t1.start();t2.start();t3.start();t4.start();t5.start();}}

结果:

注意:

当多个线程访问MyThread的run方法时,以排队的方式进行处理(这里排队是按照CPU分配的先后顺序而定的) ​ 一个线程想要执行synchronized修饰的方法里的代码 ​ 1.尝试获得锁 ​ 2.如果拿到锁,执行synchronized代码内容:拿不到锁,这个线程就会不断尝试获得这把锁,直道拿到为止, ​ 而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题:前面线程count不是按顺序的)

这篇关于理解线程安全:保护你的代码免受并发问题困扰的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1052828

相关文章

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

购买磨轮平衡机时应该注意什么问题和技巧

在购买磨轮平衡机时,您应该注意以下几个关键点: 平衡精度 平衡精度是衡量平衡机性能的核心指标,直接影响到不平衡量的检测与校准的准确性,从而决定磨轮的振动和噪声水平。高精度的平衡机能显著减少振动和噪声,提高磨削加工的精度。 转速范围 宽广的转速范围意味着平衡机能够处理更多种类的磨轮,适应不同的工作条件和规格要求。 振动监测能力 振动监测能力是评估平衡机性能的重要因素。通过传感器实时监

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

缓存雪崩问题

缓存雪崩是缓存中大量key失效后当高并发到来时导致大量请求到数据库,瞬间耗尽数据库资源,导致数据库无法使用。 解决方案: 1、使用锁进行控制 2、对同一类型信息的key设置不同的过期时间 3、缓存预热 1. 什么是缓存雪崩 缓存雪崩是指在短时间内,大量缓存数据同时失效,导致所有请求直接涌向数据库,瞬间增加数据库的负载压力,可能导致数据库性能下降甚至崩溃。这种情况往往发生在缓存中大量 k

客户案例:安全海外中继助力知名家电企业化解海外通邮困境

1、客户背景 广东格兰仕集团有限公司(以下简称“格兰仕”),成立于1978年,是中国家电行业的领军企业之一。作为全球最大的微波炉生产基地,格兰仕拥有多项国际领先的家电制造技术,连续多年位列中国家电出口前列。格兰仕不仅注重业务的全球拓展,更重视业务流程的高效与顺畅,以确保在国际舞台上的竞争力。 2、需求痛点 随着格兰仕全球化战略的深入实施,其海外业务快速增长,电子邮件成为了关键的沟通工具。

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提