【并发】共享模型之管程

news2025/1/29 13:53:27

共享模型之管程

共享问题

package 并发;

public class Test1 {
    static int a=0;
    public static void main(String[] args) throws InterruptedException {

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<5000;i++){
                    a++;
                }

            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
               for(int i=0;i<5000;i++){
                   a--;
               }
            }
        });
        t1.start();;
        t2.start();
        t1.join();;
        t2.join();
        System.out.println("a="+a);
    }

}

与预期的结果不同

问题分析

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

例如:对于i++而言,实际会产生如下的JVM字节码指令:

getstatic i //获取静态变量 iconst_1 //准备常量1 iadd //自增 putstatic i //将修改后的值存入静态变量i

而JAVA 的内存模型如下,完成静态变量的自增,自减需要在主存和工作内存中进行数据交换:

如果是单线程,上面的代码是顺序执行(不会交错) 没有问题:

临界区

  • 一个程序运行多个线程本身是没有问题的。
  • 问题出在多个线程访问共享资源。
    • 多个线程读取共享资源其实也没有问题
    • 在多个线程对共享资源读写操作时发生指令交集,就会出现问题。
  • 一般代码块如果存在对共享资源的多线程读写操作。那么这段代码称为临界区。
Thread t1=new Thread(new Runnable() {
    @Override
    public void run() {
     //临界区
       a++;

    }
});
Thread t2=new Thread(new Runnable() {
    @Override
    public void run() {
    //临界区
      a--;
    }
});

解决方案

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

本次课程使用的解决方案式:synchronzied ,来解决上述问题,俗称【对象锁】。

它采用互斥的方式让同一时刻至多只有一个线程能持有【对象锁】,其他想获取这个对象锁就会被阻塞住,这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心上下文的切换。

注意:

虽然java中的互斥和同步都是可以采用synchronized来完成,但还是有区别的。

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

synchronzied

语法
synchronized(){  
    临界区
}
解决
package 并发;

import java.util.Date;

public class Test1 {
    static Integer a=0;
    static Object flag=new Object();
    public static void main(String[] args) throws InterruptedException {

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<5000;i++){
                    //加锁
                    synchronized (flag){
                        a++;
                    }
                }

            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<5000;i++){
                      //加锁
                    synchronized (flag){
                        a--;
                    }
                }
            }
        });
        t1.start();;
        t2.start();
        t1.join();;
        t2.join();
        System.out.println("a="+a);
    }

}
向对象思想改进面
package 并发;

import java.util.Date;

 class Test1 {
    static Integer a=0;
    static   Room room=new Room();


    public static void main(String[] args) throws InterruptedException {

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<5000;i++){

                        room.increase();;

                }

            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<5000;i++){

                        room.decrease();;


                }
            }
        });
        t1.start();;
        t2.start();
        t1.join();;
        t2.join();
        System.out.println("结果是"+room.count);
    }

}





class Room {
    public static int count=0;
    static Object flag=new Object();
    public void increase(){
        synchronized (flag){
            count++;
        }

    }

    public void decrease(){

        synchronized (flag){
            count--;
        }
    }



}

方法上的synchronized

语法

synchronized加在普通方法上

class Room {
    public static int count=0;
    static Object flag=new Object();
    public synchronized void increase(){

            count++;
        

    }
    //等价于 锁住的是自己的对象
      public void increase(){
        synchronized(this){
            count++;
        }

    }

}

synchronized加在静态方法上

class Room {
    public static int count=0;
    static Object flag=new Object();
    public synchronized  static void increase(){

            count++;
        

    }
    //等价于 锁住的是自己的类对象
      public static void increase(){
        synchronized(Room.class){
            count++;
        }

    }

}

不加synchronized方法无法保证原子性

线程安全分析

成员变量和静态变量是否是安全的?

  • 如果他们没有共享,则线程安全
  • 如果他们被共享了,根据他们的线程是否能改变,又分为两种:

                只有读操作,则线程安全。

                如果有读写操作,则这段代码是临界区,需要考虑线程安全。

局部变量是否是线程安全的?

  • 局部变量是线程安全的
  • 但局部变量引用的对象未必。(堆中的变量就可能被共享)
    • 如果该对象没有逃离方法的作用范围,则是线程安全的。
    • 如果该对象逃离方法的作用范围,则需要考虑线程安全
局部变量线程安全分析

public static void test1(){ int i=10; i++; }

每个线程调用test1()方法时局部变量i,会在每个线程的栈帧内存中被创建多份,因此不存在共享

如图:

局部变量的引用稍有不同

先看一个成员变量的例子

class ThreadUnsafe { 
    ArrayList list = new ArrayList<>(); 

public void method1(int loopNumber) { 
    for (int i = 0; i < loopNumber; i++) { 
    // { 临界区, 会产生竞态条件 method2(); method3();



执行其中一种情况是,如果线程2 还未 add,线程1 remove 就会报错:Exception in thread "Thread1" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.rangeCheck(ArrayList.java:657) at java.util.ArrayList.remove(ArrayList.java:496) at cn.itcast.n6.ThreadUnsafe.method3(TestThreadSafe.java:35) at cn.itcast.n6.ThreadUnsafe.method1(TestThreadSafe.java:26) at cn.itcast.n6.TestThreadSafe.lambda$main$0(TestThreadSafe.java:14) at java.lang.Thread.run(Thread.java:748)分析:无论哪个线程中的 method2 引用的都是同一个对象中的 list 成员变量method3 与 method2 分析相同 // } 临界区 } } private void method2() { list.add("1"); } private void method3() { list.remove(0); }}

执行

static final int THREAD_NUMBER = 2;
static final int LOOP_NUMBER = 200;
public static void main(String[] args) { 
    ThreadUnsafe test = new ThreadUnsafe(); 
    for (int i = 0; i < THREAD_NUMBER; i++) { 
    new Thread(() -> { test.method1(LOOP_NUMBER); }, "Thread" + i).start(); }}

其中一种情况是,如果线程2 还未 add,线程1 remove 就会报错

Exception in thread "Thread1" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.rangeCheck(ArrayList.java:657) at java.util.ArrayList.remove(ArrayList.java:496) at cn.itcast.n6.ThreadUnsafe.method3(TestThreadSafe.java:35) at cn.itcast.n6.ThreadUnsafe.method1(TestThreadSafe.java:26) at cn.itcast.n6.TestThreadSafe.lambda$main$0(TestThreadSafe.java:14) at java.lang.Thread.run(Thread.java:748)

分析:

无论哪个线程中的 method2 引用的都是同一个对象中的 list 成员变量

method3 与 method2 分析相同

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

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

相关文章

Linux scp命令 服务器之间通讯

目录 一. scp命令简介二. 本地服务器文件传输到远程服务器三. 本地服务器文件夹传输到远程服务器 一. scp命令简介 scp&#xff08;Secure Copy Protocol&#xff09;是用于在Unix或Linux系统之间安全地复制文件或目录的命令。 它使用SSH&#xff08;Secure Shell&#xff09;…

python基础-文件读写

总结&#xff1a; 文件操作掌握一个函数open&#xff0c;三个方法read,write,close; 1、操作文件的思路 打开文件&#xff1b; 注意&#xff1a; 计算机操作文件的步骤基本固定&#xff01;读取文件&#xff1b; 关闭文件&#xff1b; 2、 操作文件 在Python中…

【数据库】聊聊MVCC机制与BufferPool缓存机制

上一篇文章&#xff0c;介绍了隔离级别&#xff0c;MySQL默认是使用可重复读&#xff0c;但是在可重复读的级别下&#xff0c;可能会出现幻读&#xff0c;也就是读取到另一个session添加的数据&#xff0c;那么除了配合使用间隙锁的方式&#xff0c;还使用了MVCC机制解决&#…

信息系统安全——Linux 访问控制机制分析

实验 4 Linux 访问控制机制分析 4.1 实验名称 《Linux 访问控制机制分析》 4.2 实验目的 1 、熟悉 Linux基本访问控制机制使用和原理 2 、熟悉 Linux S 位的作用和使用 3 、熟悉强制访问控制 Selinux 原理及其使用 4.3 实验步骤及内容 1 、Linux 基本访问控制机制 &#xff08…

Harbor离线安装

下载安装包 $ wget https://github.com/goharbor/harbor/releases/download/v2.7.4/harbor-offline-installer-v2.7.4.tgz解压 $ tar xvf harbor-offline-installer-v2.7.4.tgz -C /usr/local修改配置 $ cd /usr/local/harbor $ cp harbor.yml.tmpl harbor.yml $ vim harbo…

c++多久会被Python或者新语言取代?

c多久会被Python或者新语言取代&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「c的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&am…

【保姆级】教师资格证介绍,教资考什么,应该准备什么,5分钟快速浏览(包含报考流程)

官网 一般一年两次&#xff0c;通过笔试之后可以报名面试 这里写目录标题 报考教资的报考条件笔试科目一&#xff0c;科目二科目三 面试笔试面试通过后需要进行申请拿到教师资格证申请需要的材料1.毕业证书2.普通话3.笔试面试合格4.体检检查合格5.思想品德鉴定6.其他证件 黑龙…

mybatisplus(service CRUD 接口)

一、我们在控制器层都是调用Service层&#xff0c;不会直接调用仓储层。现在我给大家介绍一下怎么快速实现Service 的CRUD 定义接口&#xff1a;IProductService 继承IService<实体> package com.saas.plusdemo;import com.baomidou.mybatisplus.extension.service.ISe…

VSCode添加Python解释器并安装Python库

目录 一、安装VSCode 二、安装Python解释器 1、安装包链接 2、安装过程 3、测试 4、安装flake8和yapf两个包 &#xff08;1&#xff09;安装flake8包 &#xff08;2&#xff09;安装yapf包 三、VSCode中选择python解释器 一、安装VSCode VSCode安装教程&#xff08;默…

【iOS】UIColor、CGColor、CIColor的区别和联系

编者在实验室小组的指导下&#xff0c;仿写了许多App&#xff0c;其中UI的颜色模仿也是令人头痛的点。设计颜色一般使用UIColor类方法直接获取颜色&#xff1a; 有时会使用 (UIColor *)colorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alph…

GPT编程:运行第一个聊天程序

环境搭建 很多机器学习框架和类库都是使用Python编写的&#xff0c;OpenAI提供的很多例子也是Python编写的&#xff0c;所以为了方便学习&#xff0c;我们这个教程也使用Python。 Python环境搭建 Python环境搭建有很多种方法&#xff0c;我们这里需要使用 Python 3.10 的环境…

2023年终总结,一路向阳待花期

回望2023&#xff0c;可谓“苦尽甘来终有时&#xff0c;一路向阳待花期”。这一年&#xff0c;经历很多&#xff0c;收获亦很多。 回望2023 2023年最重要的三件事&#xff0c;想聊聊买房、工作、自我提升。 买房&#xff1a; 众所众知&#xff0c;2023楼市整体的情况不甚乐…

【SpringBoot实战专题】「开发实战系列」深入迁出探索剖析SpringBoot服务容器特性的利器之Actuator(Web端点)

深入迁出探索剖析SpringBoot服务容器特性的利器之Actuator 内容简介内容大纲Actuator Web端点洞察应用程序内部状况的关键Actuator提供了13个端点启用ActuatorMaven依赖Gradle依赖 Actuator透视组件装配过程获得Bean装配报告&#xff08;/beans&#xff09;Bean属性概览Bean报告…

代码随想录算法训练营第一天|数组理论基础、704二分查找、27移除元素

数组理论基础 一维数组 数组中的元素在内存空间中是连续的数组名与数组中第一个元素的地址相同&#xff08;一维数组&#xff09;数组的下标从0开始删除数组的元素其实是用后面的元素覆盖掉要删除的元素数组的长度不能改变 二维数组 二维数组是按照行存储的&#xff0c;也是…

宠物空气净化器品牌推荐哪个牌子好?五款猫用空气净化器高质量推荐品牌

养宠人家里除了猫粮、猫砂和罐头等必备的日常用品外&#xff0c;宠物空气净化器也是必需的。它可以在我们不方便开窗通风的日子里&#xff0c;有效净化室内空气&#xff0c;并且能够有效减少动物皮屑引起的过敏反应。然而&#xff0c;面对市场上琳琅满目的新款空气净化器、功能…

Python--循环语句

在 Python 中&#xff0c;循环语句用于重复执行一段代码多次。Python 主要提供了两种类型的循环&#xff1a;for 循环和 while 循环。 1. for 循环 for 循环用于遍历可迭代对象&#xff08;如列表、元组、字典、字符串等&#xff09;中的每个元素&#xff0c;并对每个元素执行…

【牛客周赛Round 27】题目讲解

题目一 小红的二进制删数字&#xff1a; 小红拿到了一个二进制字符串 s&#xff0c;她可以删掉其中的一些字符&#xff0c;使得最终该字符串为一个2的幂&#xff08;即可以表示为 2^k 形式的数&#xff09;。小红想知道&#xff0c;自己最少删几个字符可以达成&#xff1f;请你…

哈希表的实现(2):拉链法实现哈希表

一&#xff0c;拉链法 在使用线性探测法实现哈希表时&#xff0c;会发生哈希冲突。这个时候就得向后找位置给新插入的值。这个过程无疑会对哈希表的效率有很大的影响。那我们能不能通过另一种方式来实现哈希表&#xff0c;让哈希表不会发生哈希冲突呢&#xff1f;答案当然是可以…

海外媒体宣发:新闻媒体发稿引爆社交媒体的7个诀窍-华媒舍

社交媒体的崛起已经改变了新闻媒体的传播方式。从Facebook到Twitter&#xff0c;从Instagram到LinkedIn&#xff0c;社交媒体平台为新闻媒体提供了一个巨大且潜力无限的受众群体。要在这个竞争激烈的环境中引爆社交媒体&#xff0c;需要一些技巧和诀窍。在本篇文章中&#xff0…

代码随想录算法训练营day8|344.反转字符串、541.反转字符串II、54.替换数字、151.翻转字符串里的单词、55.右旋转字符串

344.反转字符串 541. 反转字符串II 卡码网&#xff1a;54.替换数字 151.翻转字符串里的单词 卡码网&#xff1a;55.右旋转字符串 344.反转字符串 建议&#xff1a; 本题是字符串基础题目&#xff0c;就是考察 reverse 函数的实现&#xff0c;同时也明确一下 平时刷题什么时候用…