java基础(多线程)-共享模型之管程

news2024/12/24 8:43:55

一、共享资源带来的问题

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/645186.html

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

相关文章

基于html+css的图展示126

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

苹果手机之间如何互传照片?批量传输操作指南

很多时候&#xff0c;我们用手机拍摄了好看的照片或者收藏了一些有趣的图片&#xff0c;想要分享给朋友&#xff0c;却不知道苹果手机之间如何互传照片&#xff1f;在分享大量照片的时候不清楚如何批量操作&#xff1f;别担心&#xff0c;下面小编就来分享一下苹果手机照片传输…

LeetCode279. 完全平方数 DP完全背包

https://leetcode.cn/problems/perfect-squares/ 题目描述 给你一个整数 n &#xff0c;返回 和为 n 的完全平方数的最少数量。 完全平方数 是一个整数&#xff0c;其值等于另一个整数的平方&#xff1b;换句话说&#xff0c;其值等于一个整数自乘的积。例如&#xff0c;1、4…

python类中常用的魔术方法

文章目录 构造方法__init__对象转字符__str__对象自定义大小比较First__lt__Second__le__Third__eq__ 构造方法__init__ 构造方法也是魔术方法的一种&#xff0c;此方法我在python对象与类中已经展示过了 注意&#xff1a;在方法中引用类成员变量一定要记得使用self关键字引用…

Elasticsearch:倒数排序融合 - Reciprocal rank fusion

警告&#xff1a;此功能处于技术预览阶段&#xff0c;可能会在未来版本中更改或删除。 Elastic 将尽最大努力修复任何问题&#xff0c;但技术预览中的功能不受官方 GA 功能的支持 SLA 约束。 倒数排序融合&#xff08;RRF&#xff09;是一种将具有不同相关性指标的多个结果集组…

蓝牙资讯|Canaly发布2023Q1全球可穿戴腕带设备报告

根据市场调查机构 Canalys 公布的最新报告&#xff0c;2023 年第 1 季度全球可穿戴腕带设备出货量为 4100 万台&#xff0c;同比下降 1%。 其中&#xff0c;本季度全球基础手环市场受到厂商和消费者更关注大屏设备影响&#xff0c;出货量为 750 万台&#xff0c;同比下降 24%…

Vue中如何进行拖拽与排序功能实现

Vue中如何进行拖拽与排序功能实现 在Web应用程序中&#xff0c;拖拽和排序功能是非常常见的。在Vue中&#xff0c;我们可以使用一些组件库来实现这个功能&#xff0c;例如sortablejs和vuedraggable。本文将介绍如何使用vuedraggable组件来实现Vue中的拖拽和排序功能。 什么是v…

Selenium Python教程第7章:Selenium编程其它功能

7. Selenium其它功能 7.1 Action Chains 动作链功能 WebDriver只能模拟针对特定元素的click, send_keys 操作&#xff0c;无法执行鼠标移动、悬浮、按键&#xff0c;选择菜单等操作&#xff0c;需要通过 Action Chains 类来操作 如下面操作&#xff0c;打开主菜单项后&#x…

实战:用dockerfile创建镜像实现springboot项目容器化

文章目录 前言技术积累docker基本操作命令dockerfile简介dockerfile指令说明 实战演示创建dockerfile创建挂载目录构建dockerfile启动容器完成验证 写在最后 前言 docker容器化方案是当下流行的服务部署方式&#xff0c;在软件领域举足轻重。我公司的测试、线上环境都采用dock…

选择最适合你的云服务器:腾讯云、华为云、阿里云对比

云服务器是一种基于云计算技术的服务器&#xff0c;它可以为企业提供高效、灵活、安全的运行环境。目前市场上有很多云服务器的选择&#xff0c;其中腾讯云、华为云和阿里云是最受欢迎的三个品牌&#xff0c;下面我们来看看它们各自的优势。 腾讯云的优势在于其强大的技术支持…

谷粒商城p45-自动装配-stream流-lambda表达式

软件&#xff1a;idea、postman、virtual box 服务&#xff1a;gulimall-product 请求路径&#xff1a;http://localhost:10000/product/category/list/tree 启动&#xff1a;启动idea product模块&#xff0c;启动vm&#xff0c;启动docker mysql controller代码 自动装配C…

LeetCode494. 目标和 0-1背包DP

https://leetcode.cn/problems/target-sum/ 题目描述 给你一个整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 ‘’ 或 ‘-’ &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式 &#xff1a; 例如&#xff0c;nums [2, 1] &#xff0c;可以在…

网络传输中的那些编码之-UTF8编码漫谈

编码为什么是一个重要的话题&#xff0c;因为我们和计算机交互的主要方式目前还是文字字符。作为一个程序员&#xff0c;相信大部分都都被字符和编码的问题折磨过&#xff0c;从键盘输入文字字符到编辑器 中&#xff0c;编辑器存储字符到硬盘&#xff0c;再到具体一个编程语言如…

中国电子学会2023年05月份青少年软件编程Scratch图形化等级考试试卷二级真题(含答案)

2023-05 Scratch二级真题 题数&#xff1a;37 分数&#xff1a;100 测试时长&#xff1a;60min 一、单选题(共25题&#xff0c;共50分) 1.运行下列哪段程序&#xff0c;可以让狗狗走到木屋门口&#xff1f;&#xff08;C&#xff09;(2分) A. B. C. D. 答案解析&a…

我用AI提高我的代码质量,周边同事对我的代码赞不绝口,速来围观

文章目录 前言功能演示1.使用Stream API来简化集合操作2.使用switch语句来替代多个if-else语句3.使用try-with-resources语句来自动关闭资源4. Lambda 表达式来简化代码,并提高代码的可读性和可维护性5.查找代码中的bug并优化6.python 使用sort方法来对列表进行排序7.javaScrpi…

一文看懂MySQL是什么

你可以前往我的博客查看更多关于云服务器和数据库以及域名注册、建站等相关文章。 MySQL是一种开源关系型数据库管理系统&#xff0c;它是最受欢迎的数据库之一。MySQL是由瑞典公司MySQL AB创建的&#xff0c;后来被Sun Microsystems收购&#xff0c;现在是Oracle Corporation…

Flink 系列二 Flink 状态化流处理概述

本篇作为Flink系列的第二篇&#xff0c;第一篇是环境准备&#xff0c;需要的同学可以看&#xff1a;https://blog.csdn.net/lly576403061/article/details/130358449?spm1001.2014.3001.5501。希望可以通过系统的学习巩固该方面的知识&#xff0c;丰富自己的技能树。废话不多说…

解决 org.eclipse.jface.text.Document class file version 61.0 报错

问题描述 运行好好的项目&#xff0c;没有做任何改动&#xff0c;最近在编译时报以下错误 java.lang.UnsupportedClassVersionError: org/eclipse/jface/text/Document has been compiled by a more recent version of the Java Runtime (class file version 61.0), this vers…

解决Python使用pip安装库文件出现“ERROR: Cannot unpack file…”

解决问题 1 ERROR: Could not find a version that satisfies the requirement robotframework (from versions: none) ERROR: No matching distribution found for robotframework 在dos命令输入 pip install robotframework 在线安装robotframework 如下报错&#xff1a; …

在紧急情况下,120可以定位我们的位置吗

随着科技的快速发展&#xff0c;越来越多的人们开始意识到科技对于生活的重要性。在现代社会中&#xff0c;GPS定位系统已经成为了一个不可或缺的工具&#xff0c;并且被广泛应用于各个领域&#xff0c;包括医疗救援行业。 120急救车和120急救指挥调度系统都采用了GPS定位技术…