java基础(多线程)-共享资源并发问题以及synchronized解决方案

news2025/2/23 13:14:39

一、共享资源带来的问题

class ThreadProblem{
    static int counter = 0;
    public static void testThread(){
        Thread t1 = new Thread(()-> {
            for (int i = 0; i < 5000; i++) {
               counter++;
            }
        },"t1");

        Thread t2 = new Thread(()-> {
            for (int i = 0; i < 5000; i++) {
                counter--;
            }
        },"t2");

        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(counter);
    }

}

1.1 问题分析

以上的结果可能是整数、负数、零。为什么呢?因为java中对静态变量的自增自减并不是原子操作,要彻底理解,必须从字节码进行分析。

例如对于i++而言(i为静态变量),实际会产生如下的JVM字节码指令:

getstatic   i   //获取静态变量i的值

iconst_1       //准备变量1

1add            //自增

putstatic      //把修改后的值存入静态变量i

 

 在多线程下这8行代码(8行代码指i++,i--的JVM字节码)可能交错运行:

1.2 临界区 Critical Section

  • 一个程序运行多个线程本身没有问题的
  • 问题出在铎哥 线程访问共享资源
  1.  多个线程读共享资源其实也没有问题
  2.  在多个线程对共享资源读写操作时发生指令交错,就会出现问题
  • 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区
static int counter = 0;
static void increment(){
    //临界区
    counter++;
}

1.3 竞态条件 Race Condition

多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件。

二、synchronized解决方案

2.1 应用之互斥

为了避免临界区的竞态条件发生,有多种手段可以达到目的。

  • 阻塞式的解决方案:synchronized,Lock
  • 非阻塞式的解决方案:原子变量 

本篇文章使用阻塞式的解决方案:synchronized,来解决竞态条件的发生,即俗称的【对象锁】,它采用互斥的方式让同一个时刻至多只有线程能持有【对象锁】,其他线程再想获取这个【对象锁】就会被阻塞住。这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心上下文切换。

注意:

虽然java中互斥和同步都可以采用synchronized关键字来完成,但他们还是有区别的:

  • 互斥是保证临界区的竞态条件发生,同一时刻只能有一个线程执行临界区代码
  • 同步是由于线程执行的先后顺序不同,需要一个线程等待其他线程运行到某个点

2.2 synchronized语法

synchronized(对象){//对象可以使任意的,但要保证操作同一个【共享资源】使用同一个对象

        临界区

}

class ThreadSynchronized{
    static int counter = 0;
    static Object room = new Object();
    public static void testSynchronized(){
        Thread t1 = new Thread(()-> {
            for (int i = 0; i < 5000; i++) {
                synchronized(room){
                    counter++;
                }
            }
        },"t1");

        Thread t2 = new Thread(()-> {
            for (int i = 0; i < 5000; i++) {
                synchronized(room) {
                    counter--;
                }
            }
        },"t2");

        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(counter);
    }
}

2.3  synchronized的理解

做一个类比:

  • synchronized(对象) 中的对象,可以想象成一个房间(room),有唯一的入口(门),房间只能一次进入一人进行计算,线程t1,t2想象成两个人。
  • 当线程t1执行到synchronized(room)时就好比t1进入了这个房间,并锁住了门拿走了钥匙,在门内执行counter++代码。
  • 这时候如果t2也运行到了synchronized(room)时,它发现门被锁住了,只能在门外等待,发生了上下文切换,阻塞住了。
  • 这中间即使t1的cpu时间片不幸用完,被踢出了门外(不要错误的理解为锁住了对象就能一直执行下去哦),这时门还是被锁住的,t1仍然拿着钥匙,t2线程还在阻塞状态进不来,只有下次CPU时间片分配给t1,t1再次获得时间片才能开门进入。
  • 当t1执行完synchronized(room)块内的代码,这时候才会从obj房间出来,并揭开门上的锁,唤醒t2等其他线程,其他某个线程拿到钥匙才可以进入obj房间,锁住门拿着钥匙,执行它的counter--代码。

 2.4 synchronized总结

synchronized实际是用对象锁保证了临界区内代码的原子性,临界区内的代码对外是不可分割的,不会被线程切换所打断。

2.5 锁对象面向对象的改进

把需要保护的共享变量放入一个类

class Room{
    private int value=0;
    public void increment(){
        synchronized (this){
            value++;
        }
    }
    public void decrement(){
        synchronized (this){
            value--;
        }
    }
    public int get(){
        synchronized (this){
            return value;
        }
    }
}

 三、方法上的synchronized

public synchronized void decrement(){
     value--;
}
相当于,锁对象是this对象
public void decrement(){
    synchronized (this){
        value--;
    }
}

public synchronized static void decrement(){
     value--;
}
相当于,锁对象是类对象
public void decrement(){
    synchronized (Room.class){
        value--;
    }
}

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

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

相关文章

【VMD-LSTM】变分模态分解-长短时记忆神经网络研究(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

主动降噪技术的运用及其展望---【其利天下技术】

降噪耳机想必大家是听说过的&#xff0c;那么什么是降噪耳机呢&#xff1f;降噪耳机的降噪是如何实现的呢&#xff1f; 在很多年前&#xff0c;我想大家肯定认为降噪耳机不就是做得比较帖耳&#xff0c;尽量把声波能量隔离不让进入人耳吗&#xff1f;搞得这么神秘干吗呢&#…

猿辅导基于 EMR StarRocks 的 OLAP 演进之路

摘要&#xff1a;猿辅导大数据平台团队负责人申阳分享了猿辅导基于 StarRocks 的 OLAP 演进之路。主要包括以下几大部分&#xff1a; 数据需求产生OLAP 选型StarRocks 的优势业务场景和技术方案基础建设 Tips&#xff1a;点击「阅读原文」查看原文视频 1► 数据需求产生 猿辅导…

【Linux】进程间通信(1)——匿名管道

文章目录 前言进程间通信的目的进程间通信的发展进程间通信分类管道什么是管道&#xff1f;站在内核角度-管道本质匿名管道pipe函数管道的特点&#xff08;重要&#xff09;用fork来共享管道原理匿名管道的使用步骤管道的读写规则管道的四种场景 如何使用管道进行进程间通信&am…

【Vue3学习】Vuex 状态管理 store

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 库。它采用集中式存储管理应用的所有组件的状态&#xff0c;并以相应的规则保证状态以一种可预测的方式发生变化。 安装 npm npm install vuexnext --saveyarn npm install vuexnext --save基本使用 1&#xff09;创建…

Kubernetes初认识

系列文章目录 文章目录 系列文章目录一、Kubernetes初认识1.k8s的特性2.K8S架构3.Kubernetes工作流程 二、K8S创建Pod流程1.详细版2.简单版 总结 一、Kubernetes初认识 1.k8s的特性 弹性伸缩&#xff1a;使用命令、UI或者基于CPU使用情况自动快速扩容和缩容应用程序实例&…

磁盘调度算法及其应用

导读&#xff1a; 磁盘调度是计算机系统中的重要问题之一。在多个进程同时访问磁盘时&#xff0c;合理的磁盘调度算法可以优化磁盘访问顺序&#xff0c;提高系统性能。本文将介绍磁盘调度算法的基本思想&#xff0c;并通过一个实验来模拟不同调度算法的运行过程。 正文&#…

如何翻译 Markdown 文件?-2-几种商业及开源解决方案介绍

背景 近期在搭建英文博客-<e-whisper.com>, 需要对现有的所有中文 Markdown 翻译为英文。 需求如下&#xff1a; 将 Markdown 文件从中文 (zh-CN) 翻译为英文 (en)翻译后要保留 Markdown 的完整格式部分 Markdown block 不需要翻译&#xff0c;如&#xff1a;front-ma…

电脑蓝屏问题

如何使用DISM命令行工具修复Windows 10映像 - 系统极客 (sysgeek.cn) 电脑每周基本都会出现一次蓝屏问题&#xff1a;THREAD_STUCK_IN_DEVICE_DRIVER 售后重装系统&#xff0c;换主板&#xff0c;换硬盘都没有用&#xff0c;实在是人麻了 不知道有没有用&#xff0c;先记录一…

“边玩边赚”的区块链游戏发展前景和趋势

2008年&#xff0c;一个真实的故事。 大学期间&#xff0c;睡在我下铺的兄弟没日没夜地玩一款电脑游戏——《热血江湖》&#xff0c;期末考试和考研都没能阻止他。而最终&#xff0c;是游戏里的一把枪让他改邪归正。因为他把那件装备卖给了一个北京人&#xff0c;价格高达3000…

IT专业相关介绍【活动】

IT专业相关介绍【活动】 前言IT专业相关介绍一、IT专业的就业前景和发展趋势二、了解IT专业的分类和方向三、你对本专业的看法和感想四、本专业对人能力素养的要求五、建议和思考六、计算机思维能力测试 最后 前言 2023-6-17 10:00:29 以下内容源自《【活动】》 仅供学习交流…

Spring-kafka的消费者模型和实现原理

在使用Spring-kafka时,一般都是通过使用@KafkaListener注解的方法来实现消息监听和消费。今天写一下基于这个注解实现的消费端模型和实现的原理。 Kafka消费模型 我们在使用@KafkaListener注解实现消费者时消费者模型是这样的: 每个@KafkaListener注解对应有一个Concurren…

python窗口程序button事件处理

import tkinter as tk def add_counter(): #增加计数print("add....")def zero_counter(): #归零计数print("zero....")#窗口的属性&#xff08;大小&#xff0c;&#xff09; root tk.Tk() root.geometry("400x200200200") root.title(&q…

亚马逊云科技中国峰会:深度学习Amazon DeepRacer

序言 Amazon DeepRacer是什么&#xff1f; Amazon DeepRacer是亚马逊推出的一款基于深度学习和强化学习技术的自主驾驶模拟赛车平台。它提供了一个云端仿真环境和一个物理赛车模型&#xff0c;让用户可以通过编写代码和训练模型来控制赛车的行驶&#xff0c;从而学习和应用深…

【LeetCode】HOT 100(14)

题单介绍&#xff1a; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&#xff0c;适合初识算法与数据结构的新手和想要在短时间内高效提升的人&#xff0c;熟练掌握这 100 道题&#xff0c;你就已经具备了在代码世界通行的基本能力。 目录 题单介绍&#…

音视频开发Level0: 入门级20~25k的工作

今天给大家分享一个音视频开发领域&#xff0c;入门级别的工作&#xff0c;要求不高。 主要做什么呢&#xff0c;行车记录仪&#xff0c;运动相机&#xff0c;各种拍摄器材包括医疗领域的喉镜啊&#xff0c;等等。 这种产品&#xff0c;招人的公司深圳最多&#xff0c;因为深…

Mac 多版本jdk安装与切换

macOS上可以安装多个版本的jdk&#xff0c;方法如下&#xff1a; 1.下载jdk 在Oracle官网上下载不同版本的jdk&#xff1a; JDK下载 知乎 - 安全中心 下载Java11版本链接 jdk11​www.oracle.com/java/technologies/javase-jdk11-downloads.html 2.安装jdk 运行此安装包&…

electron-vue 安装 sqlite3 详细步骤

1 安装 Visual Studio 2019 使用 Visual Studio instaler 安装Visual Studio 2019&#xff0c; 安装桌面应用 使用c的桌面开发, 勾选 MSVC 相应的选项。 2. 安装 node 13 版本 可以根据自己实际情况安装版本 使用 cmd 管理员身份或者 powerShell 管理员身份 执行以下命令&…

骨传导蓝牙立体声耳机怎么选,列举几款值得购买的骨传导耳机

骨传导耳机的出现&#xff0c;使得很多人摆脱了佩戴入耳式耳机的困扰&#xff0c;同时也为骨传导耳机的发展起到了很大的推动作用。骨传导耳机是一种通过骨头传声的耳机&#xff0c;由于其不需要入耳&#xff0c;所以不会因为长时间佩戴而引起耳道的不适感&#xff0c;在使用时…

baichuan-7B: 开源可商用支持中英文的最好大模型

背景 baichuan-7B 是由百川智能开发的一个开源可商用的大规模预训练语言模型。 基于 Transformer 结构&#xff0c;在大约1.2万亿 tokens 上训练的70亿参数模型&#xff0c;支持中英双语&#xff0c;上下文窗口长度为4096。 在标准的中文和英文权威 benchmark&#xff08;C-…