线程和线程安全

线程安全和线程工具 本文介绍了线程安全的核心概念及实现方法。线程安全问题主要源于竞态条件、内存可见性和指令重排序。常用的线程安全工具包括:synchronized关键字(同步方法和代码块)、volatile关键字(保证可见性)、ReentrantLock和ReadWriteLock(灵活锁机制)、原子类(CAS操作)、线程安全集合类(如ConcurrentHashMap)。文章还分析了常见面试题,如synchronized与ReentrantLock的区别、volatile作用、CAS优缺点、线程池参数、死锁避免方法、Thre 阅读详情

进程的概念

正在运行中的程序就是进程;解释如下图

线程的概念

进程中的一个独立的执行路径。解释如下图

线程的分类

单线程:同一时间只能干一件事.(多件事只能等一个处理完成后才能开始处理下一个)

多线程:同一时间能干多件事情。(可以辅助线程的并行理解)

主线程:程序启动系统自动创建并执行main方法的线程。主线程的执行入口:main方法 (说起主线程在这里顺便提一下  守护线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)

子线程:除了主线程以为的其他线程,子线程的执行入口:run方法

线程的常用方法

void setName(String name)
String getName();
static Thread currentThread();
static void sleep(long mills)


线程的五种状态

新建:刚刚创建出来,还没start

就绪:调用start方法或睡醒或唤醒

运行:抢到CPU使用权

阻塞:调用sleep或wait方法

死亡:调用stop方法或任务正常执行完毕。

什么是线程安全?

当多个线程访问某个方法时,不管你通过怎样的调用方式、或者说这些线程如何交替地执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的;

搞清楚了什么是线程安全,接下来我们看看Java中确保线程安全最常用的两种方式。先来看段代码。

public void threadMethod(int j) {

    int i = 1;

    j = j + i;
}

大家觉得这段代码是线程安全的吗?

毫无疑问,它绝对是线程安全的,我们来分析一下,为什么它是线程安全的?

我们可以看到这段代码是没有任何状态的,就是说我们这段代码,不包含任何的作用域,也没有去引用其他类中的域进行引用,它所执行的作用范围与执行结果只存在它这条线程的局部变量中,并且只能由正在执行的线程进行访问。当前线程的访问,不会对另一个访问同一个方法的线程造成任何的影响。

两个线程同时访问这个方法,因为没有共享的数据,所以他们之间的行为,并不会影响其他线程的操作和结果,所以说无状态的对象,也是线程安全的。

那么添加一个状态呢?

如果我们给这段代码添加一个状态,添加一个count,来记录这个方法并命中的次数,每请求一次count+1,那么这个时候这个线程还是安全的吗?

public class ThreadDemo {

   int count = 0; // 记录方法的命中次数

   public void threadMethod(int j) {

       count++ ;

       int i = 1;

       j = j + i;
   }
}

很明显已经不是了,单线程运行起来确实是没有任何问题的,但是当出现多条线程并发访问这个方法的时候,问题就出现了,我们先来分析下count+1这个操作。

进入这个方法之后首先要读取count的值,然后修改count的值,最后才把这把值赋值给count,总共包含了三步过程:“读取”一>“修改”一>“赋值”,既然这个过程是分步的,那么我们先来看下面这张图,看看你能不能看出问题:

640?

可以发现,count的值并不是正确的结果,当线程A读取到count的值,但是还没有进行修改的时候,线程B已经进来了,然后线程B读取到的还是count为1的值,正因为如此所以我们的count值已经出现了偏差,那么这样的程序放在我们的代码中,是存在很多的隐患的。

如何实现线程同步呢?

既然存在线程安全的问题,那么肯定得想办法解决这个问题,怎么解决?我们说说常见的几种方式。

1、synchronized(常用)

synchronized关键字,就是用来控制线程同步的,保证我们的线程在多线程环境下,不被多个线程同时执行,确保我们数据的完整性,使用方法一般是加在方法上。

 

public class ThreadDemo {

   int count = 0; // 记录方法的命中次数

   public synchronized void threadMethod(int j) {

       count++ ;

       int i = 1;

       j = j + i;
   }
}

 

这样就可以确保我们的线程同步了,同时这里需要注意一个大家平时忽略的问题,首先synchronized锁的是花括号里的对象,而不是代码,其次,对于非静态的synchronized方法,锁的是对象本身也就是this。

当synchronized锁住一个对象之后,别的线程如果想要获取锁对象,那么就必须等这个线程执行完释放锁对象之后才可以,否则一直处于等待状态。

注意点:虽然加synchronized关键字,可以让我们的线程变得安全,但是我们在用的时候,也要注意缩小synchronized的使用范围,如果随意使用时很影响程序的性能,别的对象想拿到锁,结果你没用锁还一直把锁占用,这样就有点浪费资源。

2、Lock

先来说说它跟synchronized有什么区别吧,Lock是在Java1.6被引入进来的,Lock的引入让锁有了可操作性,什么意思?就是我们在需要的时候去手动的获取锁和释放锁,甚至我们还可以中断获取以及超时获取的同步特性,但是从使用上说Lock明显没有synchronized使用起来方便快捷。我们先来看下一般是如何使用的:

 

private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类

   private void method(Thread thread){
       lock.lock(); // 获取锁对象
       try {
           System.out.println("线程名:"+thread.getName() + "获得了锁");
           // Thread.sleep(2000);
       }catch(Exception e){
           e.printStackTrace();
       } finally {
           System.out.println("线程名:"+thread.getName() + "释放了锁");
           lock.unlock(); // 释放锁对象
       }
   }

 

进入方法我们首先要获取到锁,然后去执行我们业务代码,这里跟synchronized不同的是,Lock获取的所对象需要我们亲自去进行释放,为了防止我们代码出现异常,所以我们的释放锁操作放在finally中,因为finally中的代码无论如何都是会执行的。

写个主方法,开启两个线程测试一下我们的程序是否正常:

 

public static void main(String[] args) {
       LockTest lockTest = new LockTest();

       // 线程1
       Thread t1 = new Thread(new Runnable() {

           @Override
           public void run() {
               // Thread.currentThread()  返回当前线程的引用
               lockTest.method(Thread.currentThread());
           }
       }, "t1");

       // 线程2
       Thread t2 = new Thread(new Runnable() {

           @Override
           public void run() {
               lockTest.method(Thread.currentThread());
           }
       }, "t2");

       t1.start();
       t2.start();
   }

 

结果:

640?

可以看出我们的执行,是没有任何问题的。

其实在Lock还有几种获取锁的方式,我们这里再说一种,就是tryLock()这个方法跟Lock()是有区别的,Lock在获取锁的时候,如果拿不到锁,就一直处于等待状态,直到拿到锁,但是tryLock()却不是这样的,tryLock是有一个Boolean的返回值的,如果没有拿到锁,直接返回false,停止等待,它不会像Lock()那样去一直等待获取锁。

我们来看下代码:

 

private void method(Thread thread){
       // lock.lock(); // 获取锁对象
       if (lock.tryLock()) {
           try {
               System.out.println("线程名:"+thread.getName() + "获得了锁");
               // Thread.sleep(2000);
           }catch(Exception e){
               e.printStackTrace();
           } finally {
               System.out.println("线程名:"+thread.getName() + "释放了锁");
               lock.unlock(); // 释放锁对象
           }
       }
   }

 

结果:我们继续使用刚才的两个线程进行测试可以发现,在线程t1获取到锁之后,线程t2立马进来,然后发现锁已经被占用,那么这个时候它也不在继续等待。

640?

似乎这种方法,感觉不是很完美,如果我第一个线程,拿到锁的时间,比第二个线程进来的时间还要长,是不是也拿不到锁对象?

那我能不能,用一中方式来控制一下,让后面等待的线程,可以等待5秒,如果5秒之后,还获取不到锁,那么就停止等,其实tryLock()是可以进行设置等待的相应时间的。

 

private void method(Thread thread) throws InterruptedException {
       // lock.lock(); // 获取锁对象

       // 如果2秒内获取不到锁对象,那就不再等待
       if (lock.tryLock(2,TimeUnit.SECONDS)) {
           try {
               System.out.println("线程名:"+thread.getName() + "获得了锁");

               // 这里睡眠3秒
               Thread.sleep(3000);
           }catch(Exception e){
               e.printStackTrace();
           } finally {
               System.out.println("线程名:"+thread.getName() + "释放了锁");
               lock.unlock(); // 释放锁对象
           }
       }
   }

 

结果:看上面的代码,我们可以发现,虽然我们获取锁对象的时候,可以等待2秒,但是我们线程t1在获取锁对象之后,执行任务缺花费了3秒,那么这个时候线程t2是不在等待的。

640?

我们再来改一下这个等待时间,改为5秒,再来看下结果:

 

private void method(Thread thread) throws InterruptedException {
       // lock.lock(); // 获取锁对象

       // 如果5秒内获取不到锁对象,那就不再等待
       if (lock.tryLock(5,TimeUnit.SECONDS)) {
           try {
               System.out.println("线程名:"+thread.getName() + "获得了锁");
           }catch(Exception e){
               e.printStackTrace();
           } finally {
               System.out.println("线程名:"+thread.getName() + "释放了锁");
               lock.unlock(); // 释放锁对象
           }
       }
   }

 

结果:这个时候我们可以看到,线程t2等到5秒获取到了锁对象,执行了任务代码。

640?

以上就是使用Lock,来保证我们线程安全的方式。

Vector和List 有关线程安全和线程同步的理解 最近在学习的时候,一直很疑惑Vector和List的两个容器的区别。 在网上查了好多资料, 阅读详情

相关推荐

OC和Swift下的线程安全属性

最近看了很多关于属性线程安全的问题,即在OC中的atomic和noatomic对线程安全的理解。在property中,默认是atomic原子性的,但我们在属性的定义中一般使用noatomic,因为这种的效率是前者的20倍。但在多线程处理机的今天,我们无可避免的需要面向多线程编程,今天我们就谈谈这个atomic对属性的线程安全到底有多少影响。atomic的线程安全的目标是谁,setter和gette...

skt_sika的博客 1619

详解什么是线程安全?

并发(Concurrent),在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。那么,操作系统视如何实现这种并发的呢?现在我们用到操作系统,无论是Windows、Linux还是MacOS等其实都是多用户多任务分时操作系统。使用这些操作系统的用户是可以“同时”干多件事的。但是实际上,对于单CPU的计算机来说,在CPU中,同一时间是只能干一件事儿的。

白话机器学习 5080

Kafka延迟队列实现方案全解析

$ \text{时间轮} = \text{环形数组} + \text{分层设计} $$需要补充代码实例或特定场景的优化方案可进一步细化,此大纲已覆盖核心技术路径。

lz989796的博客 816

什么是线程安全?如何保证线程安全?

一、引入线程安全 👇二、 线程安全👇1、线程安全概念🔍2、线程不安全的原因🔍抢占式执行(罪魁祸首,万恶之源)导致了线程之间的调度是“随机的”多个线程修改同一个变量修改操作,不是原子的(不可分割的最小单位)内存可见性,引起的线程不安全指令重排序,引起的线程不安全三、解决之前的线程不安全问题👇1、synchronized 关键字-监视器锁monitor lock🔍1)synchronized 的特性(1) 互斥(2)刷新内存(3) 可重入。

leyhe的博客 6285

直流有刷电机控制 - 驱动电路(二)

1.如图所示为最常见的推挽驱动电路,我附加了具有正反转功能的电机驱动控制,正反转是由两个继电器组成。使用推挽电路来驱动NMOS管时充电电流和放电电流都很大,因此NMOS管导通快,关断也快,而且NMOS温度不会很高。下面来介绍一下该电路的原理。(1)PWM_MCU是由单片机给出的PWM波信号,该信号是由一些列高低电平的方波构成。当信号为高电平的时候Q3导通,经过R47再到Q5的g极,此时mos导通,如果是K1闭合是正转那么K2闭合就是反转。(2)当PWM低电平时使得Q3关断,Q6导通。

u011030644的博客 4719

低成本主从机械臂系统构建:6D位姿数据采集与VLA模型训练实战

本文详细介绍了如何构建低成本主从机械臂系统,实现高精度6D位姿数据采集与VLA模型训练。通过300美元级别的硬件组合,系统达到±1mm位置精度和≤0.5°姿态误差,满足Vision-Language-Action模型的多模态训练需求。文章涵盖硬件选型、数据采集技巧、模型训练实战经验,为机器人研究提供经济高效的解决方案。

r7s8t的博客 265

线程同步之可重入锁

可重入锁与不可重入锁的比较 可重入锁是指当前已经获得锁的线程可以再次获取该锁且不会陷入阻塞。这句话似乎不是很好理解,我们可以从理解不可重入锁的概念来理解。不可重入锁是指在一个线程调用某方法获取锁对象之后,其他线程再次请求获取该锁,都会陷入等待,直到当前线程释放锁之后,其他对象才有机会获得锁,并且在这期间,如果已经获得锁的线程再次执行获得锁的方法,也会使得自己被阻塞,同时释放持有的锁资源。可重入锁和...

马一凡的博客 1172

人工智能算法面试大总结-总目录

涉及秋招、春招、社招

啥都生的博客 2万+

线程安全(干货满满!)

线程安全(Thread safety)是多线程编程中的一个重要概念,它指的是在多线程环境下,一个程序或者代码段在并发访问时,能够正确地保持其预期的行为和状态,而不会出现意外的错误或者不一致的结果。具体来说,当多个线程同时访问共享资源(如共享变量、共享数据结构、共享文件等)时,线程安全的编程技术和方法旨在解决并发访问问题,确保程序的正确性和稳定性。

2302_78814792的博客 3380

使用OpenGL加载和显示DXF格式的3D模型文件编程

这样,我们就完成了使用OpenGL加载和显示DXF格式的3D模型文件的编程。通过实现加载函数和绘制回调函数,我们能够读取DXF文件的数据并使用OpenGL绘制出来。请注意,这只是一个简单的例子,仅涵盖了基本的加载和显示功能。在本文中,我们将探讨如何使用OpenGL编程加载和显示DXF(Drawing Exchange Format)格式的3D模型文件。DXF是一种通用的CAD(计算机辅助设计)文件格式,常用于存储和交换2D和3D图形数据。使用OpenGL加载和显示DXF格式的3D模型文件编程。

2301_79331421的博客 411

低轨通信 | 手机直连低轨卫星通信:架构、关键技术与未来展望

手机直连低轨卫星通信正推动人类进入"全域连接"时代。当中国星网GW星座与千帆星座的万颗卫星点亮夜空,当普通智能手机在珠峰之巅仍能流畅视频通话,当远洋渔船通过卫星物联网实现精准捕捞——这些场景不仅重构通信产业格局,更将深刻改变人类生产生活方式。正如工信部《指导意见》所规划,到2030年我国将发展超千万卫星通信用户,催生万亿级新市场[1][5]。在这场太空基建竞赛中,技术创新与标准制定的双重突破,将决定未来数字文明的星空版图。

尘世冰封的专栏 937

什么是线程安全?

线程安全 “线程安全”的定义: 当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那就称这个对象时线程安全的。 该定义要求线程安全的代码都必须具备一个共同特征: 代码本身封装了所有必要的正确性保障手段(如互斥同步等),令调用者无须关心多线程下的调用问题,更无须自己实现任何措施来保证多线程环境下的正确调用。 java语言中的线程安全 线程安全,将以多个线程之间存在共享数据访问

贾欣晓的博客 2万+

Gromacs 模拟昆虫 P450 解毒酶与农药结合完整指南,仅供参考!

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 1239

线程安全和线程不安全理解

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据 =========================================================

ghevinn欢迎您光临 7万+

基于VSCode+clangd插件打造高效C++开发环境之四:clangd配置

本文介绍如何配置VSCode+clangd插件进行高效C++开发。重点内容包括:1)生成compile_commands.json文件,通过ninja工具提取编译命令并使用gen_compdb脚本转换;2)配置clangd命令行参数,如设置后台索引、并行度等;3)编写.clangd配置文件,调整编译标志和诊断选项;4)添加.clang-format文件定义代码风格;5)在VSCode中添加远程服务器工作目录。配置完成后需重启clangd服务使更改生效。文章还提供了MTK/QCOM等不同平台生成compile

5062

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

线程安全和线程同步

线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况不可变类,如 String、基本类型的包装类、BigInteger 和 BigDecimal 等。不可变对象天生是线程安全的线程安全是指多线程环境下,当多个线程同时访问共享资源时,保证对该资源的操作能够正确地执行,并且不会出现意外的结果或破坏数据的一致性。

刘皇叔说Java的博客 1147

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

什么是线程安全和线程不安全

引入 当有多个线程共享同样的内存时: 如果 线程不会对内存进行读取和修改 内存是只读的 多个进程对这个内存进行修改,但是写是原子操作时 那么线程是安全的 当一个线程修改变量时,其他线程在读取这个变量时可能看到一个不一致的值 我们需要使用锁来同步线程:同一时间只允许一个线程访问该变量 当多个线程试图在同一时间修改同一变量时,因为修改通常不是一步到位的: 从内存中读入寄存器 在寄存器中修改变量 将新值写回内存 这个时候可能出现数据不一致,导致程序错误 线程安全:指的是多个线程在执行

OceanStar的博客 5324

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Qt 重入和线程安全

在整个文档中,"重入"和 "线程安全 "这两个术语被用来标记类和函数,以表明它们在多线程应用程序中的使用方式:线程安全重入因此,线程安全的可重入的可重入的线程安全的推而广之,如果一个类的成员函数可以被多个线程安全调用,只要每个线程使用的是该类的不同,那么这个类就是可重入的。如果可以从多个线程安全地调用类的成员函数,即使所有线程都使用类的相同,该类也是线程安全的。只有当 Qt 类被多个线程使用时,才会被记录为线程安全。如果函数未标记为线程安全或可重入,则不应在不同线程中使用。

qq_32640443的博客 1210

线程安全和锁

线程安全和锁的介绍,单例模式,阻塞队列,线程池,定时器,死锁问题

wk200411的博客 734

Java多线程 - 线程安全和线程同步解决线程安全问题

但是如果我们上锁和解锁之间的代码出现了异常, 永远都不会执行解锁操作, 所以更严谨的写法是将解锁的操作放到try…但是实际上使用任意唯一的锁对象并不好, 会影响其他无关线程的执行, 例如上面例子中, 会将其他无关的账户也锁起来。在取钱之前都需要判断余额是否足够, 例如两个线程同时执行, 两个线程都进行了余额判断, 发现余额充足;小明和小红是一对夫妻,他们有一个共同的账户,余额是10万元,模拟2人同时去取钱10万。但是代码要高度面向对象!同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。

wangluoanquan111的博客 1691

线程安全和线程存储

线程安全和线程存储和线程取消 线程安全函数:若函数可同时供多个线程安全调用,则称之为线程安全函数,如果不是线程安全函数,则不能并发调用 线程取消:向一个线程发送一个请求,要求其立即退出 /****************************** 函数功能:取消指定线程ID的的线程 返回值: On success, pthread_cancel() returns 0; on erro...

达达兔兔 196

进程和线程、线程安全

进程和线程 一个程序就是一个进程,而一个程序中的多个任务则被称为线程。 进程是表示资源分配的基本单位,线程是进程中执行运算的最小单位,亦是调度运行的基本单位。 实现多线程编程的方式有两种,一种是继承 Thread 类,另一种是实现 Runnable 接口。使用继承 Thread 类创建线程,最大的局限就是不能多继承,所以为了支持多继承,完全可以实现 Runnable 接口的方式。需要说明的是,这两种方式在工作时的性质都是一样的,没有本质的区别。 虽然系统是把资源分给进程,但是CPU很特殊,是被分配到

qq_36930483的博客 732

ArrayList 和 LinkedList 是线程安全吗

集合类型线程安全推荐方案ArrayList❌ 否或LinkedList❌ 否自己加锁 or 改用。

m0_69529796的博客 514

线程安全和可重入函数

先说结论 可重入函数未必是线程安全的;线程安全函数未必是可重入的。 可重入函数的概念 可重入的程序(函数)允许在执行的过程中被打断,并在打断所执行的代码中再次安全的调用。 若一个函数是可重入的,则该函数应当满足下述条件: 不能含有静态(全局)非常量数据。 不能返回静态(全局)非常量数据的地址。 只能处理由调用者提供的数据。 不能依赖于单实例模式资源的锁。 调用(call)的函数也必需是可重入的。 总之一句话:上述条件就是要求可重入函数使用的所有变量都通过函数的调用者提供。 举例说明,设计一个发送字符串

sinat_31039061的博客 2663
上一篇: 多线程的几种实现方式
下一篇: 尴尬了!Spring Cloud微服务注册中心Eureka 2.x停止维护了咋办?
Mr_baci
博客等级 码龄9年 1万+粉丝 68原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值