并发编程(三)原子性(1)

news2024/11/30 6:39:37

【认识原子性】:

一个小程序认识原子性:

package T05_YuanZiXing;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class T00_00_IPlusPlus {
    private static long n = 0L;

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

        //Lock lock = new ReentrantLock();

        Thread[] threads = new Thread[100];
        CountDownLatch latch = new CountDownLatch(threads.length);

        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    n++;
                }
                latch.countDown();
            });
        }

        for (Thread t : threads) {
            t.start();
        }

        latch.await();

        System.out.println(n);

    }
}

【最终输出】:
在这里插入图片描述
//启动了100个线程 , 每个线程将数字加到1W , 最后应该是100W。
【问题所在】:

在这里插入图片描述
多个线程同时拿到了变量,同时++,然后同时写回去。

【一些概念】:
race condition => 竞争条件 , 指的是多个线程访问共享数据的时候产生竞争———上述程序中变量n即是共享数据。

数据的不一致(unconsistency),并发访问之下产生的不期望出现的结果

如何保障数据一致呢?–> 线程同步(线程执行的顺序安排好),

monitor (管程) —> 锁

critical section -> 临界区

如果临界区执行时间长,语句多,叫做 锁的粒度比较粗,反之,就是锁的粒度比较细。

【修改程序】:

        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    synchronized (T00_01_IPlusPlus.class) {
                        //lock.lock();
                        n++;
                        //lock.unlock();
                    }
                }
                latch.countDown();
            });
        }

【原子操作】:

原子操作的意思就是不能够中间被打断 , 只能作为一个整体 , 不能并发执行 。

【什么样的语句是原子性的,什么样的语句不是原子性的呢?】:

不管是Java、C、C++、高级语言、低级语言 最终一定都是要搞成机器语言的。机器语言翻译过来就是汇编语言。即便是汇编语言,它执行的任何一条指令都有可能被其他的线程所打断。
【得查汇编手册】:

CPU级别汇编,需要查询汇编手册!

Java中的8大原子操作:(了解即可,无需背过)

  1. lock:主内存,标识变量为线程独占
  2. unlock:主内存,解锁线程独占变量
  3. read:主内存,读取内存到线程缓存(工作内存)
  4. load:工作内存,read后的值放入线程本地变量副本
  5. use:工作内存,传值给执行引擎
  6. assign:工作内存,执行引擎结果赋值给线程本地变量
  7. store:工作内存,存值到主内存给write备用
  8. write:主内存,写变量值

【为何不用背呢?】:

//正常的情况下 , 你判断不了的情况下。这句话到底是否具备原子性呢?——你给它上锁就可以了。

【n++翻译成汇编指令】:

在这里插入图片描述
在这里插入图片描述
1——将static的值给拿过来;
2——扔到栈空间里面;
3——将这个值++;
4——将值放回去;
5——返回。

【 用上锁保证原子性 】:

synchronized (T00_01_IPlusPlus.class) {
      n++;
}

//大括号里面的所有操作都被当作了一个整体——不可打断。

【上锁的本质(一)】:

一句高级语言翻译成机器语言可能有好多句。需要保证这些操作不被打断。

【上锁的本质(二)】:

上锁的本质就是把并发编程序列化。
【程序解释】:

package T05_YuanZiXing;

import Utils.SleepHelper;

public class T00_01_WhatIsLock {
    private static Object o = new Object();
    
    public static void main(String[] args) {
        Runnable r = () -> {
                System.out.println(Thread.currentThread().getName() + " start!");
                SleepHelper.sleepSeconds(2);
                System.out.println(Thread.currentThread().getName() + " end!");
        };

        for (int i = 0; i < 3; i++) {
            new Thread(r).start();
        }
    }
}

【最终输出】:
在这里插入图片描述
//基本上是同时启动 , 2秒之后同时结束。

【进行改进】:

package T05_YuanZiXing;

import Utils.SleepHelper;

public class T00_01_WhatIsLock_02 {
    private static Object o = new Object();


    public static void main(String[] args) {
        Runnable r = () -> {
            synchronized (o) {
                System.out.println(Thread.currentThread().getName() + " start!");
                SleepHelper.sleepSeconds(2);
                System.out.println(Thread.currentThread().getName() + " end!");
            }
        };

        for (int i = 0; i < 3; i++) {
            new Thread(r).start();
        }
    }
}

【最终输出】:
在这里插入图片描述
//红线处会等待2S , 一共执行完需要6S的时间。

【理解序列化】:


原先上厕所,三个人同时一起完成,现在要排队一个一个完成了。

【上锁的本质(三)】:

【程序演示】:

package T05_YuanZiXing;

import Utils.SleepHelper;

public class T00_02_SingleLockVSMultiLock {
    private static Object o1 = new Object();
    private static Object o2 = new Object();
    private static Object o3 = new Object();

    public static void main(String[] args) {
        Runnable r1 = () -> {
            synchronized (o1) {
                System.out.println(Thread.currentThread().getName() + " start!");
                SleepHelper.sleepSeconds(2);
                System.out.println(Thread.currentThread().getName() + " end!");
            }
        };

        Runnable r2 = () -> {
            synchronized (o2) {
                System.out.println(Thread.currentThread().getName() + " start!");
                SleepHelper.sleepSeconds(2);
                System.out.println(Thread.currentThread().getName() + " end!");
            }
        };

        Runnable r3 = () -> {
            synchronized (o3) {
                System.out.println(Thread.currentThread().getName() + " start!");
                SleepHelper.sleepSeconds(2);
                System.out.println(Thread.currentThread().getName() + " end!");
            }
        };

        new Thread(r1).start();
        new Thread(r2).start();
        new Thread(r3).start();
    }
}

【最终输出】:
//一共花了2秒。

【上锁的本质(四)】:

synchronized是可以保障可见性的,一个线程结束了,会向主内存中做同步;
synchronized保障了原子性。
但是synchronized无法保障有序性。

sync操作中的绿色区域部分的执行顺序是完全可以发生变化的。

【有序性】:

单线程保障最终一致性 , 它和锁没有关系。

【 一些同步的基本概念_锁的粒度 】:

【 monitor(管程) 】:

在这里插入图片描述
//后面跟的那一部分叫做monitor。
在这里插入图片描述
// o 即 monitor。

【 critical section( 临界区 )】:

当我持有这把锁的时候,我所执行的代码 , 它是临界区 , 是不能够两个同时在一起的,是必须顺序的,按照序列化执行的。
【实际例子解释临界区】:

在这里插入图片描述
sync大括号所包含的叫做临界区——(图中的红色区域)。
如果临界区执行的时间比较长,语句比较多 , 那我们就说锁的粒度比较粗 , 反之,就是锁的粒度比较细。

【阶段小结】:

【如何保障临界区操作的原子性呢?】:

1)乐观锁
2)悲观锁

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

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

相关文章

Android源码相关面试专题

Android源码相关面试专题 1、Android属性动画实现原理 工作原理&#xff1a;在一定时间间隔内&#xff0c;通过不断对值进行改变&#xff0c;并不断将该值赋给对象的属性&#xff0c;从而实现该对象在该属性上的动画效果。 正在上传…重新上传取消 1&#xff09;ValueAnimato…

Do Transformers Really Perform Bad for Graph Representation?

Do Transformers Really Perform Bad for Graph Representation? 论文中提出了Graphormer&#xff0c;它建立在标准的Transformer架构之上&#xff0c;并且在广泛地图表示学习任务重获得了优异的成绩。同时&#xff0c;作者也提出了一些简单但是有效的结构编码方法来帮助Grap…

【支付宝生态质量验收与检测技术】

如何验收和检测海量的支付宝生态小程序的质量&#xff0c;是一个很重要的课题。本次分享会简单介绍如何通过平台化的方式在小程序入驻环节进行准入验收&#xff0c;以及使用前端自动化测试技术和智能化算法对小程序质量进行检测。希望能对小程序质量的验收和测试提供参考。讲师…

计算机网络-应用层(应用层概述,网络应用模型(C/S模型,P2P模型),DNS域名协议)

文章目录1. 应用层概述2. 网络应用模型3. 域名系统&#xff08;DNS&#xff09;1. 应用层概述 应用层概述&#xff1a;应用层对应用程序的通信提供服务。 应用层协议定义&#xff1a; 应用进程交换的报文类型&#xff0c;请求还是响应各种报文类型的语法&#xff0c;如报文中…

分布式锁-简单入门

状态不是很好&#xff0c;记一下以前学过的分布式锁吧。 样例简介 不谈大概念&#xff0c;就是简单入门以及使用。 为什么要用分布式锁呢&#xff1f; 假设我需要一个定时操作&#xff0c;每天在某个点&#xff0c;我要处理一批数据&#xff0c;要先从数据库中查询出来&…

云计算-Hadoop-2.7.7 最小化集群的搭建(3台)

云计算-Hadoop-2.7.7 最小化集群的搭建&#xff08;3台&#xff09; 文章目录云计算-Hadoop-2.7.7 最小化集群的搭建&#xff08;3台&#xff09;一、环境依赖下载二、部署概要三、hadoop101模板机配置1. 更新 & 升级2. 安装好用的vim VimForCpp3. 安装必要依赖4. 关闭防火…

nginx配置https访问 生成ssl自签名证书,浏览器直接访问

问题 nginx配置自签名ssl证书&#xff0c;来支持https访问nginx&#xff0c;在浏览器中访问nginx时&#xff0c;提示有风险。而访问其他各大网站时&#xff0c;也是使用了https协议&#xff0c;为什么可以直接访问&#xff0c;而不提示有风险呢&#xff1f; 解疑 先从ssl证书…

MyBatis--动态SQL

Emp类 1.if标签 通过test属性中的表达式判断标签中的内容是否有效 (是否会拼接到SQL中) 接口 映射 测试 2.Where标签 where标签的三个作用 若where标签中有条件成立 , 会自动生成where关键字会自动将where标签中内容前多余的and去掉 , 但是其中内容后多余的and无法去掉若where标…

mysql explain和DESC性能分析

mysql explain和DESC 根据执行时间去只可以粗略的判断sql的性能&#xff0c;我们如果想去查看一条sql语句的性能还需要explain去查看sql的执行计划。 EXPLAIN 或者 DESC 命令获取 MySQL 如何执行 SELECT 语句的信息&#xff0c;包括在 SELECT 语句执行过程中表如何连接和连接的…

如何做好供应商绩效管理?

供应商绩效管理是一种商业行为&#xff0c;用于衡量、分析和管理供应商的绩效。供应商管理专业人员寻求削减成本&#xff0c;减轻风险并推动持续改进。企业可使用供应商管理系统来监测供应商的绩效水平。 供应商绩效管理最佳实践 所有企业都必须发展核心竞争力&#xff0c;有…

【Linux 网络编程 】

Linux 网络编程背景知识&#xff1a;主机字节序列和网络字节序列IP地址的转换API网络编程接口网络节序与主机节序转换函数IP地址转换函数数据读写TCP编程编程步骤&#xff1a;客户端链接服务端成功的条件多线程实现服务端并发多进程实现服务端并发注意&#xff1a;UDP编程编程步…

自动化测试基础简介(本质)

目录 前言 1.自动化基础 2.分层的自动化测试 2.1 单元自动化测试 2.2 接口自动化测试 2.3 UI自动化测试 3.适合自动化的项目 4.自动化测试模型 4.1线性测试 4.2模块化与类库 4.3数据驱动测试 4.4关键字驱动测试 5.POM设计模式 总结 前言 随着软件系统规模的日益…

应对Redis缓存污染问题,你应该知道这些内容

前言 我们在使用Redis做为缓存时&#xff0c;能加速我们对于热点数据的查询。但是如果缓存中有大量的数据不再热门了&#xff0c;从而占据着大量的内存空间&#xff0c;那么我们的Redis性能就会收到很大影响。该如何解决这个问题呢&#xff1f;本文给你答案。 什么是缓存污染…

kafka开发环境搭建

1 kafka开发环境 1.1 安装Java环境 1.1.1 下载linux下的安装包 登陆网址https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 下载完成后&#xff0c;Linux默认下载位置在当前目录下的Download或下载文件夹下&#xff0c;通过命令cd ~/…

轻松学习string类常用接口(附模拟实现)

目录 String的常用接口说明(最常用的) string类对象的容量操作 string类对象的访问及遍历操作 string类对象的修改操作 string类非成员函数 深浅拷贝 简介&#xff1a;Cstring 是C中的字符串。 字符串对象是一种特殊类型的容器&#xff0c;专门设计来操作的字符序列。 不像…

MySQL 全文检索的实现

微信搜「古时的风筝」&#xff0c;还有更多技术干货 这有朋友聊到他们的系统中要接入全文检索&#xff0c;这让我想起了很久以前为一个很古老的项目添加搜索功能的事儿。 一提到全文检索&#xff0c;我们首先就会想到搜索引擎。也就是用一个词、一段文本搜索出匹配的内容。一般…

Vue3中的组合Api与响应函数

文章目录1. 组合Api介绍setup2. 响应函数2.1 ref2.2 reactive2.3 toRef和toRefs2.4 readonly2.5 customRef1. 组合Api介绍 组合Api其实时用于解决功能、数据和业务逻辑分散的问题&#xff0c;使项目更益于模块化开发以及后期维护。 vue2.x — optionsApi 配置式Api — react类…

STM32 定时器定时计算

STM32 定时器定时计算STM32 定时器频率例子公式referenceSTM32 定时器频率 定时时间 定时器频率 / 倍频 /装载周期 htim1.Init.Prescaler 72-1;htim1.Init.CounterMode TIM_COUNTERMODE_UP;htim1.Init.Period 1*1000*1000;tim 72x10^6 / (72-1)/ 110001000 1us 根据定时…

RAC/RAC One Node 修改私网/心跳网卡名

cDAS RAC/RAC One Node一般采用ib ip作为私网/心跳ip&#xff0c;有些现场可能会有修改心跳/私网网卡的需求&#xff0c;一般修改网卡也会伴随修改心跳/私网ip的需求。若心跳/私网有高可用&#xff0c;可以一个个修改。 场景1&#xff1a;心跳网卡有高可用 原心跳网卡名 原心…

Vue中 引入使用 localforage 改进本地离线存储(突破5M限制)

1. 简介 说到本地存储数据&#xff0c;首先想到的是 localStorage&#xff0c;应该很多小伙伴都用过&#xff0c;使用很简单。然而&#xff0c;localStorage 却有下面一些缺点&#xff1a; 存储容量限制&#xff0c;大部分浏览器应该最多5M。我就遇到过localStorage存储字符然…