Java多线程编程学习——02线程安全问题

java多线程——线程安全-举例 之所以会输出8、9、7,是因为进程内的所有线程都可以访问到该区域,当第一个线程已经获得9这个数了,还没来得及输出,下一个线程在这段时间的空隙获得了8这个值,故输出时会输出8、9的顺序值。线程安全性的一个重要方面是确保对共享资源的访问是原子的,即不可分割的,要么全部完成,要么全部不完成。如果忽略了线程安全性,可能会导致难以调试和重现的错误,这些错误可能只在特定的线程调度或系统负载下才会出现。因此,开发者需要仔细考虑并设计线程安全的代码,使用适当的同步机制,并对共享资源进行适当的访问控制。 阅读详情



什么是线程安全

如果有多个线程同时运行同一个实现了Runnable接口的类,程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的;反之,则是线程不安全的。


线程安全问题根本原因

多个线程同一时刻操作共享的数据,会导致该共享数据出现与预期的值不符的情况,这样便会引发线程安全问题。


问题解决-线程同步

要解决线程安全问题,只要在某个线程修改共享资源的时候,其他线程不能修改该资源,等待该线程修改完毕同步之后,其他线程才能去抢夺CPU资源,完成对应的操作,保证了数据的同步性,解决了线程不安全的现象。

为了保证每个线程都能正常执行共享资源操作,Java引入了7种线程同步机制。

  1. 同步代码块(synchronized)
  2. 同步方法(synchronized)
  3. 同步锁(ReenreantLock)
  4. 特殊域变量(volatile)
  5. 局部变量(ThreadLocal)
  6. 阻塞队列(LinkedBlockingQueue)
  7. 原子变量(Atomic*)

同步代码块(synchronized)

synchronized 关键字可以用于方法中的某个区域中,表示只对这个区块的资源实行互斥访问。

同步锁: 对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.

  1. 锁对象可以是任意类型。
  2. 多个线程要使用同一把锁。

注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLOCKED)。

同步代码块代码

public class MyThread05 implements Runnable {

    private int tickNum = 200;

    Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (tickNum > 0) {
                    //1.模拟出票时间
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //2.打印进程号和票号,票数减1
                    String name = Thread.currentThread().getName();
                    System.out.println("线程" + name + "售票:" + tickNum--);
                }
            }
        }
    }

    public static void main(String[] args) {
        MyThread05 myThread=new MyThread05();
        Thread thread01 = new Thread(myThread,"窗口1");
        Thread thread02 = new Thread(myThread,"窗口2");
        Thread thread03 = new Thread(myThread,"窗口3");
        thread01.start();
        thread02.start();
        thread03.start();
    }
}

同步方法(synchronized)

同步方法:

使用synchronized修饰的方法,就叫做同步方法,保证当前线程执行该方法的时候,其他线程无法访问该方法,因为该方法已经被锁定,直到当前线程运行完毕释放资源。

同步锁是谁?

  1. 对于非static方法,同步锁就是this。
  2. 对于static方法,同步锁是当前方法所在类的字节码对象(类名.class)。

同步方法代码

public class MyThread06 implements Runnable {

    private int tickNum = 100;

    Object obj = new Object();

    @Override
    public void run() {
        //使用while(true)的作用:
        //
        //run方法中的代码就是线程要运行的代码,运行完毕以后,就不会再次运行,
        // 其方法本身并不是无限循环的。而while(true)是为了让run方法中的代码不断重复的运行,
        // 也就是让线程不停的运行,便于查看效果。如果去掉,run运行结束,线程也就结束了
        while (true) {
            getTick();
        }
    }

    public synchronized void getTick() {
        while (true) {
            if (tickNum > 0) {
                //1.模拟出票时间
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //2.打印进程号和票号,票数减1
                String name = Thread.currentThread().getName();
                System.out.println("线程" + name + "售票:" + tickNum--);
            }
        }
    }

    public static void main(String[] args) {
        MyThread06 myThread = new MyThread06();
        Thread thread01 = new Thread(myThread, "窗口1");
        Thread thread02 = new Thread(myThread, "窗口2");
        Thread thread03 = new Thread(myThread, "窗口3");
        thread01.start();
        thread02.start();
        thread03.start();
    }
}

同步锁(ReenreantLock)

同步锁

java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。

同步锁方法

  1. public void lock() :加同步锁。
  2. public void unlock() :释放同步锁。

同步锁代码

public class MyThread07 implements Runnable {
    private int tickNum = 100;

    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (tickNum > 0) {
                    //1.模拟出票时间
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //2.打印进程号和票号,票数减1
                    String name = Thread.currentThread().getName();
                    System.out.println("线程" + name + "售票:" + tickNum--);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        MyThread07 myThread = new MyThread07();
        Thread thread01 = new Thread(myThread, "窗口1");
        Thread thread02 = new Thread(myThread, "窗口2");
        Thread thread03 = new Thread(myThread, "窗口3");
        thread01.start();
        thread02.start();
        thread03.start();
    }
}

Synchronized和Lock区别
  1. synchronized是java内置关键字;在jvm层面,Lock是个java类。
  2. synchronized无法判断是否获取锁的状态;Lock可以判断是否获取到锁。
  3. synchronized会自动释放锁,当前线程执行完同步代码会释放锁,若线程执行过程中发生异常会释放锁);Lock需在finally中手动释放锁(unlock()方法释放锁),否则容易造成线程死锁。
  4. synchronized关键字加锁,如果当前线程获得锁,则其他线程等待。如果当前线程阻塞,其他线程则会一直等待下去;而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了。
  5. synchronized的锁可重入、不可中断、非公平;而Lock锁可重入、可判断、可公平(两者皆可)
  6. Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
Java多线程编程的那些坑:线程安全与性能优化 在现代的企业级应用中,多线程编程已成为实现高效并发处理的关键技术之一。Java作为一门广泛应用于后端开发的语言,其多线程编程为开发者提供了强大的并发支持。然而,虽然Java多线程编程提供了许多便捷的工具和库,开发者在实际开发过程中仍然面临许多挑战。特别是在涉及线程安全和性能优化时,若不小心,程序可能会陷入一些潜在的“坑”中,导致死锁、竞态条件、线程泄漏等问题。本文将结合实践,探讨Java多线程编程中的常见坑,尤其是线程安全和性能优化方面的挑战,以及如何避免这些问题。 阅读详情

相关推荐

Java并发编程中的线程安全问题与解决方案

线程安全问题指的是多个线程在同时访问共享资源时,可能出现数据不一致、脏读、覆盖更新等问题。例如,一个线程修改变量,另一个线程读取时,可能得到不正确的结果。Executors提供的线程池虽然方便,但默认参数可能不适用于所有场景。推荐使用自定义线程池,可以更精细地控制线程池行为。

shrgegrb的博客 1127

ReentrantLock获取锁及释放锁的详情

NonFairSync在获取锁的时候,首先通过CAS进行了一次竞争锁的操作,如果竞争到了,那么就结束lock()方法。如果没有竞争到就和FairSync获取锁一样了,进入AQS的acquire(1)方法;

guojinpeng112的博客 1196

手把手教你用Arduino IDE玩转ESP32-S3-wroom(附串口调试技巧)

本文详细介绍了如何在Arduino IDE环境下配置和使用ESP32-S3-WROOM模块,包括开发环境搭建、硬件连接、程序烧录及高级调试技巧。通过实战步骤和常见问题解决方案,帮助开发者快速上手这一高性能Wi-Fi/蓝牙双模模组,提升物联网开发效率。

weixin_30487201的博客 292

ReentrantLock介绍

ReentrantLock介绍

WYSCODER的博客 4040

FPGA实战:手把手教你用Verilog驱动4.3寸RGB触摸屏(附完整代码)

本文详细介绍了如何使用Verilog语言在FPGA上驱动4.3寸RGB触摸屏,包括时序设计、信号交互原理及完整代码实现。通过双计数器引擎设计和参数化模块,开发者可以高效构建800*480分辨率的显示驱动,并解决工程实践中的信号完整性和时序收敛问题。

rice5的博客 702

手把手教学:利用AutoDL与vLLM快速部署DeepSeek-R1大模型实战指南

本文详细介绍了如何利用AutoDL平台和vLLM框架快速部署DeepSeek-R1大模型的实战指南。从服务器配置、vLLM安装优化到模型部署的两种方案,再到性能调优和生产环境建议,手把手教你高效完成大模型部署。特别针对国内开发者遇到的网络问题提供了稳定解决方案,并分享了显存优化、磁盘清理等实用技巧。

weixin_42531779的博客 327

ReentrantLock主要了解

ReentantLock 继承接口 Lock 并实现了接口中定义的方法,他是一种可重入锁,除了能完 成 synchronized 所能完成的所有工作外,还提供了诸如可响应中断锁、可轮询锁请求、定时锁等 避免多线程死锁的方法。 Lock 接口的主要方法 1. void lock(): 执行此方法时, 如果锁处于空闲状态, 当前线程将获取到锁. 相反, 如果锁已经 被其他线程持有, 将禁用当前线程, 直到当前线程获取到锁. 2. boolean tryLock():如果锁可用, 则获取锁, 并立即返回 true

罗格的博客 2230

去面试游戏测试工程师,面试官看着我的简历呆住了

!我个人整理了我这几年软件测试生涯整理的一些技术资料,包含:电子书,简历模块,各种工作模板,面试宝典,自学项目等。欢迎大家点击下方名片免费领取,千万不要错过哦。

MXB1220的博客 7329

可重入锁ReentrantLock详解

https://www.cnblogs.com/zhengbin/p/6503412.html

wangqiubo2010的博客 245

从 SM59 到 SPROXY,打通 ABAP 后端与 ES Repository 的代理生成链路

本文探讨了在SAP PI/PO项目中配置ABAP后端系统与ES Repository连接的关键步骤,重点解决ABAP Proxy生成时无法获取接口模型的问题。文章指出: 该配置本质是建立设计时通道,使ABAP系统能通过SPROXY访问ES Repository中的接口模型并生成代理类 配置核心是在SM59创建名为SAP_PROXY_ESR的HTTP连接(类型G),需确保: 目标路径为/rep 使用具备SAP_XI_IS_SERV_USER角色的服务账号 连接测试通过后仍需在SPROXY中验证完整功能 常见陷

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 61

科研必备:15款顶尖AI科研工具全面分析汇总(持续更新ing)

科研必备:15款AI科研工具全面分析汇总(持续更新ing)

Code_流苏:在代码中寻诗意,在实践中觅真知 9652

线程安全问题 线程同步

线程安全问题,同步1.线程安全问题1.1.什么是线程安全1.2.问题演示1.2.1.第一步:创建售票线程类1.2.2.第二步:创建测试类1.3.问题分析1.4.问题解决-线程同步1.4.1.同步代码块(synchronized)1.4.2.同步方法(synchronized)1.4.3.同步锁(ReenreantLock)标题1.5.小结 哔哩哔哩大神的视频整理 1.线程安全问题 1.1.什么是线程安全 如果有多个线程同时运行同一个实现了Runnable接口的类,程序每次运行结果和单线程运行的结果是一样的,

weixin_45229007的博客 404

qt open3dBPA重建

open3d BPA重建

阁楼 516

Java多线程之线程安全问题

本篇文章介绍的内容为Java多线程中的线程安全问题,此处的安全问题并不是指的像黑客入侵造成的安全问题,线程安全问题是指因多线程抢占式执行而导致程序出现bug的问题。

未见花闻的博客 9593

uniapp [安卓苹果App端] - 详细实现手机蓝牙连接打印机及打印票据小票/标签/面单/热敏小票订单/快递单等通用兼容适配解决方案,Uniapp对接蓝牙打印机支持各种主流品牌及打印指令,强悍插件

uni-app,安卓Android,苹果ios,安卓app端,苹果App端,uniapp苹果app,蓝牙打印机,uniappAPP实现通过蓝牙连接打印机打印,uniapp蓝牙连接打印机,安卓蓝牙打印插件,蓝牙打印机,蓝牙ble低功率及高功率打印机小票、标签、面单打印,手机PDA蓝牙打印插件,连接蓝牙打印机,蓝牙连接,打印文本、图片、条码、二维码,兼容斑马打印机、佳博打印机,TSC打印机,免费蓝牙打印机组件,蓝牙打印条码,蓝牙打印机连接,热敏蓝牙打印机,便携式打印机,打印机搜索不到,连接失败,发送打印命令

🏆 前端领域优质创作者 1万+

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Java 多线程编程中如何保证线程安全?

线程安全是指在多线程环境下,对共享资源的访问和操作不会导致数据不一致、程序崩溃或其他不可预期的结果。也就是说,多个线程同时访问和修改共享资源时,程序的行为仍然符合预期,如同单线程环境下执行一样。例如,一个计数器类在单线程环境下可以正常工作,但在多线程环境中,如果多个线程同时对计数器进行自增操作,可能会出现计数不准确的问题,而线程安全的计数器类则能避免这种情况。这些类使用 CAS(Compare-And-Swap)算法实现,能够在不使用锁的情况下保证对共享变量的原子操作。实现线程安全的方法:使用同步机制。

qq_45626589的博客 1382

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

深入理解 Java 中的多线程编程:解决线程安全问题

在 Java 多线程编程中,线程安全是一个关键问题。本文介绍了多种解决线程安全问题的方法,包括使用Lock接口、Atomic变量等。每种方法都有其适用的场景和优缺点。适合简单的同步需求,易于使用,但可能影响性能。Lock提供了更灵活的锁控制,但需要显式地获取和释放锁。Atomic变量适合轻量级的线程安全数值操作,性能较高。理解这些技术的适用场景,可以帮助开发者编写高效且安全的多线程程序。

HJKQS的博客 861

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

java多线程编程(二)一一>线程安全问题, 单例模式, 解决程线程安全问题的措施

如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。

robin_suli的博客 1538

Java多线程之线程安全问题_java 线程安全

我们知道操作系统中线程程的调度是抢占式执行的, 宏观上上的感知是随机的, 这就导致了多线程在进行线程调度时线程的执行顺序是不确定的, 因此多线程情况下的代码的执行顺序可能就会有无数种, 我们需要保证这无数种线程调度顺序的情况下, 代码的执行结果都是正确的, 只要有一种情况下, 代码的结果没有达到预期, 就认为线程是不安全的, 对于多线程并发时会使程序出现BUG的代码称作线程不安全的代码, 这就是线程安全问题.

2401_89694232的博客 1064

Java多线程编程中如何有效避免线程安全问题

通过Lock接口,我们可以实现更细粒度的锁控制,例如尝试获取锁(tryLock)、定时获取锁(tryLock(long time, TimeUnit unit))以及可中断的获取锁(lockInterruptibly)等操作。线程池是一种用于管理和复用线程的机制。通过合理地配置线程池的大小和任务队列,可以有效地控制并发线程的数量,避免过多的线程竞争资源导致性能下降和线程安全问题。例如,可以使用局部变量或线程本地存储(ThreadLocal)来存储线程私有的数据,避免多个线程对同一数据的并发访问。

derickForster的博客 710

【Java并发编程学习 2】多线程线程安全问题

什么是线程安全 当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。 案例:需求现在有100张火车票,有三个窗口同时抢火车票,请使用多线程模拟抢票效果。 package com.lijie; public class ThreadTrain implements Runnable { private int...

小杰爱吃蛋的博客 831

Java多线程编程中的常见问题

Java多线程编程中常见的问题与陷阱包括线程安全问题、死锁、线程饥饿与活锁、线程池配置不当、并发集合误用、线程中断处理不当、内存可见性问题、线程创建与销毁不当、线程优先级设置不当、同步过度、资源竞争、异常处理不当、线程调度问题以及性能问题。解决这些问题的方法包括使用同步机制、合理配置线程池、使用线程安全集合、正确处理线程中断、使用volatile关键字、优化同步范围、使用资源池、捕获并处理异常、选择合适的线程调度策略以及使用性能分析工具。通过避免这些陷阱,可以提高多线程程序的稳定性和性能。

Java的专栏 1641

轻松掌握Java多线程 - 第四章:线程安全问题

线程安全是指在多线程环境下,程序能够正确地处理共享资源,确保数据的一致性和正确性。一个线程安全的程序,无论有多少线程并发执行,都不会出现数据破坏或者得到错误结果的情况。/*** 线程安全与非线程安全的对比*/// 演示线程不安全的计数器runCounterTest("线程不安全的计数器", unsafeCounter);// 演示线程安全的计数器runCounterTest("线程安全的计数器", safeCounter);// 线程不安全的计数器count++;

qianmoQ - 关注云计算,关注大数据 1410

Java中的多线程——线程安全问题

详细了解Java中的线程安全问题

m0_56911284的博客 2045

Java多线程编程中,如何确保线程安全与资源高效利用?

因此,在实际开发中,我们需要不断地学习和探索新的技术和方法,以提高我们的编程能力和应对复杂问题的能力。可以修饰方法或代码块,当一个线程进入同步代码块或方法时,其他线程将无法进入该同步代码块或方法,直到当前线程退出同步代码块或方法。线程池维护了一定数量的线程,当需要执行新任务时,线程池会复用已有的线程,而不是创建一个新线程。例如,当线程尝试获取锁失败时,可以等待一段时间再重试,或者主动放弃CPU的使用权,让其他线程有机会执行。这些集合类内部实现了复杂的同步机制,以确保在多线程环境下对集合的访问是线程安全的。

AntonioMargery的博客 1964
上一篇: 设计模式学习——外观模式
下一篇: Java多线程编程学习——03线程死锁
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值