Java并发编程(五)线程同步 下 [CAS/原子类/同步容器类/同步工具类]

CAS

概述

CAS全称为Compare-And-Swap。它是一条CPU的原子指令,是硬件对于并发操作共享数据的支持。其作用是CPU在某个时刻比较两个值是否相等

核心原理:在操作期间CAS先比较下主存中的值和线程中工作内存中的值是否相等,如果相等才会将主存中的值更新为新值,不相等则不交换(如果不相等则会一直通过自旋方式尝试更新值)

CAS指令存在如下问题:

  • ABA问题:两个时刻比较值都会存在ABA问题,原来是A,中间变成B,又变回A,CAS检测认为值没有发生变化,但实际上确实发生变化了。
    • 解决方案:JDK1.5中的AtomicStampedReference可以用来解决ABA问题。基本思路是增加版本号,修改的当前值和逾期值是否一致。AtomicStampedReference是通过当前引用和逾期的引用是否相等,来进行CAS操作。
  • 循环时间长开销大:如果CAS失败,则会一直通过自旋进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销。因此冲突如果过于频繁的场景不建议使用CAS原语进行处理(CAS是乐观锁机制,乐观锁不适合冲突频繁的场景,这时候可以选择悲观锁的机制)。

CAS算法示例

package com.bierce;

public class TestCAS {
    public static void main(String[] args) {
        final CAS cas = new CAS();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                int expectedValue = cas.getValue();//每次更新前必须获取内存中最新的值,因为可能与compareAndSwap中第一次获取的Vaule不一样
                boolean updateRes = cas.compareAndSet(expectedValue, (int) Math.random() * 101);
                System.out.print(updateRes + " "); // true true true true true true true true true true
            }).start();
        }
    }
}
class CAS{
    private int value;
    //获取内存值
    public synchronized  int getValue(){
        return value;
    }
    //比较内存值
    public synchronized  int compareAndSwap(int expectedValue, int newValue){
        int oldValue = value;//
        if (oldValue == expectedValue){
            this.value = newValue;
        }
        return oldValue;
    }
    //设置内存值
    public synchronized  boolean compareAndSet(int expectedValue, int newValue){
        return expectedValue == compareAndSwap(expectedValue, newValue);
    }
}

原子类

概述

在多线程环境下我们可以通过加锁(synchronized/Lock)保证数据操作的一致性,但效率显得过于笨重。因此java提供java.util.concurrent.atomic包,里面是原子操作的封装类,使用起来方便且高性能高效。Atomic 类方法没有任何加锁,底层核心就是通过 volatile 保证了线程可见性以及Unsafe类的CAS算法操作保证数据操作的原子性

分类

java.util.concurrent.atomic包下的原子类

AtomicInteger实践

package com.bierce;
import java.util.concurrent.atomic.AtomicInteger;
public class TestAtomicDemo {
    public static void main(String[] args) {
        AtomicDemo atomicDemo = new AtomicDemo();

        //模拟多线程,通过原子类保证变量的原子性
        for(int i =0; i < 10; i++){
            new Thread(atomicDemo).start();
        }
    }
}
class AtomicDemo implements Runnable{
    //AtomicInteger原子类提供很多API,根据需求使用
    private AtomicInteger sn = new AtomicInteger(); 
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //getAndIncrement() :类似于 i++
        //incrementAndGet() :类似于 ++i
        //System.out.print(sn.getAndIncrement() + " "); // 输出:1 9 8 7 0 5 2 6 4 3
        System.out.print(sn.incrementAndGet() + " "); // 输出:1 10 9 5 2 8 3 6 7 4
    }
}

同步容器类

  • ConcurrentHashMap: 同步的HashMap的并发实现
  • ConcurrentSkipListMap: 同步的TreeMap的并发实现
  • CopyOnWriteArrayList:适合于并发读取或者遍历集合数据需求远大于列表更新需求时
  • ThreadLocal: 利用空间换时间思想,为每个线程复制一个副本,通过隔离线程实现线程安全
  • BlockingQueue
  • ConcurrentLinkedQueue: 通过CAS保证线程安全,采用延时更新策略,提高吞吐量

ConcurrentHashMap

ConcurrentHashMap内部采用了“锁分段”思想替代HashTable的独占锁,增加了同步的操作来控制并发,且提高了性能,但JDK1.7&&JDK1.8版本中底层实现有所区别

  • JDK1.7版本: 底层为ReentrantLock+Segment+HashEntry,JDK1.8版本: 底层为synchronized+CAS+HashEntry+红黑树
  • JDK1.8中降低了锁的粒度:JDK1.7版本锁的粒度是基于Segment的,包含多个HashEntry,而JDK1.8锁的粒度就是HashEntry(首节点)
  • JDK1.8中数据结构更加简单:JDK1.8使用synchronized来进行同步,所以不需要Segment这种数据结构
  • JDK1.8使用红黑树来优化链表:基于长度很长的链表的遍历是一个很漫长的过程,而红黑树的遍历效率是很快
  • 扩展:为何用synchronized来代替reentrantLock
    • 因为粒度降低了,synchronized并不比ReentrantLock差;在粗粒度加锁中ReentrantLock可能通过Condition来控制各个低粒度的边界而更加灵活,而在低粒度中,Condition的优势就没有了
    • JVM的开发团队从来都没有放弃synchronized,而且基于JVM的synchronized优化空间更大,使用内嵌的关键字比使用API更加自然
    • 在大量的数据操作下,对于JVM的内存压力,基于API的ReentrantLock会开销更多的内存

CopyOnWriteArrayList实践

package com.bierce;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * CopyOnWriteArrayList : 支持并发读取时写入
 * 注意:添加数据操作多时,效率比较低,因为每次添加都会进行复制一个新的列表,开销大;
 *      而并发迭代操作多时效率高
 */
public class TestCopyOnWriteArrayList {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayListDemo copyOnWriteArrayListDemo = new CopyOnWriteArrayListDemo();

        for (int i = 0; i < 10; i++) {
            new Thread(copyOnWriteArrayListDemo).start();
        }
    }
}
class CopyOnWriteArrayListDemo implements  Runnable{
    // 不支持并发修改场景,Exception in thread "Thread-1" java.util.ConcurrentModificationException
    //private static List<String> list = Collections.synchronizedList(new ArrayList<String>());
    public static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
    static{
        list.add("贾宝玉");
        list.add("林黛玉");
        list.add("薛宝钗");
    }
    @Override
    public void run() {
        Iterator<String> iterator = list.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
            list.add("test");
        }
    }
}

同步工具类

  • 倒计时器CountDownLatch
  • 循环栅栏CyclicBarrier
  • 资源访问控制Semaphore

CountDownLatch(闭锁)

概述

  • CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,用来实现线程间的通信
  • CountDownLatch适用于一个线程在等待另外一些线程完成各自工作之后,再继续执行的场景

原理

CountDownLatch底层是通过一个计数器来实现的,计数器的初始化值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就相应的减1。当计数器到达0时,表示所有的线程都已完成任务,然后在闭锁上等待的线程就可以继续执行任务

实践

package com.bierce;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.CountDownLatch;

public class TestCountDownLatchDemo {
    public static void main(String[] args) {
        final CountDownLatch countDownLatch = new CountDownLatch(5);//声明5个线程
        CountDownLatchDemo countDownLatchDemo = new CountDownLatchDemo(countDownLatch);

        //long start = System.currentTimeMillis();
        Instant start = Instant.now();//JDK8新特性API
        for (int i = 0; i < 5; i++) { //此处循环次数必须和countDownLatch创建的线程个数一致
            new Thread(countDownLatchDemo).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
        }
        //long end = System.currentTimeMillis();
        Instant end = Instant.now();
        //System.out.println("执行完成耗费时长: " + (end - start));
        System.out.println("执行完成耗费时长: " + Duration.between(end, start).toMillis()); // 110ms
    }
}
class CountDownLatchDemo implements Runnable{
    private CountDownLatch countDownLatch;
    public CountDownLatchDemo(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void run() {
        synchronized (this){ //加锁保证并发安全
            try {
                for (int i = 0; i < 10000; i++) {
                    if (i % 2 == 0) {
                        System.out.println(i);
                    }
                }
            }finally {
                countDownLatch.countDown(); //线程完成后执行减一
            }
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3.2 Tomcat基础

1. Tomcat概述 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器&#xff0c;属于轻量级应用服务器。 Tomcat版本&#xff1a;apache-tomcat-8.5.76。 2.IDEA集成Tomcat 第一步 第二步 第三步 ​ 编辑切换为居中 添加图片注释&#xff0c;不超过 140 字&#xff0…

【JavaEE进阶】SpringBoot 配置文件

文章目录 SpringBoot配置文件1. 配置文件的作用2. 配置文件的格式3. properties 配置文件说明3.1 properties 基本语法3.2 读取配置文件3.3 properties 优缺点分析 4. yml配置文件说明4.1 yml 基本语法4.2 yml 配置读取 5. properties和yml的对比 SpringBoot配置文件 1. 配置文…

002-Spring boot 自动配置相关分析

目录 自动配置 EnableAutoConfiguration开启自动配置读取配置提前过滤自动配置配置包 AutoConfigurationPackage 自动配置 EnableAutoConfiguration 开启自动配置 在Spring 启动类上的 SpringBootApplication 中有 EnableAutoConfiguration 读取配置 Import(AutoConfigurat…

【福建事业单位-数学运算】04计算、最值和几何

【福建事业单位-数学运算】04计算、最值和几何 一、计算1.1 基础计算1.2 数列计算等差数列等比数列 总结 二、最值问题2.1 最不利构造最不利加排列组合 2.2 构造数列 三、几何问题2.1 公式计算类规则图形非规则图形 2.2结论技巧性&#xff08;三角形&#xff09;总结 一、计算 …

easyx图形库基础:1.基本概念的介绍+图形的绘制。

基本概念的介绍图形的绘制 一.基本概念的介绍。1.为什么要使用easyx图形库2.安装easyx图形库。3.语法相关 二.图形绘制1.窗体创建和坐标的概念。1.基本窗体的创建。2.坐标概念3.改变逻辑坐标。 2.设置图形颜色1.设置描边颜色和描边样式。2.设置图形填充颜色和填充样式3.绘制图形…

【Apollo】自动驾驶的平台背景,平台介绍

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

Labview控制APx(Audio Precision)进行测试测量(七)

处理集群控制子集 大多数用户不会想要设置所有的控制包括在一个大的控制集群&#xff0c;如水平和增益配置控制。例如&#xff0c;假设您只在 APx 中使用模拟不平衡输出连接器&#xff0c;而您想要做的就是控制发电机的电平和频率。在这种情况下&#xff0c;水平和增益配置集群…

Visual Studio 2019 详细安装教程(图文版)

前言 Visual Studio 2019 安装包的下载教程、安装教程 教程 博主博客链接&#xff1a;https://blog.csdn.net/m0_74014525 关注博主&#xff0c;后期持续更新系列文章 ********文章附有百度网盘安装包链接********* 系列文章 第一篇&#xff1a;Visual Studio 2019 详细安装教…

MAUI+Blazor:隐藏标题栏和问题

文章目录 前言相关文章代码问题有必要解决吗&#xff1f; 前言 最近在研究MAUIBlazor开发&#xff0c;发现一个问题&#xff0c;原生的的标题栏实在是太丑了。 相关文章 MAUI桌面端标题栏设置和窗口调整 MAUI Windows How to completely hide the TitleBar? #15142 MAUI …

【第一阶段】kotlin中反引号中的函数名特点

在kotlin中可以直接中文定义函数&#xff0c;使用反引号进行调用 eg: fun main() {2023年8月9日定义的函数(5) }private fun 2023年8月9日定义的函数(num:Int){println("反引号的用法$num") }执行结果 在Java中is,in可以定义方法&#xff0c;但是在kotlin中is,in是…

【设计模式】桥接模式

桥接&#xff08;Bridge&#xff09;是用于把抽象化与实现化解耦&#xff0c;使得二者可以独立变化。这种类型的设计模式属于结构型模式&#xff0c;它通过提供抽象化和实现化之间的桥接结构&#xff0c;来实现二者的解耦。 这种模式涉及到一个作为桥接的接口&#xff0c;使得…

构建Docker容器监控系统(cadvisor+influxDB+grafana)

目录 一、部署 1、安装docker-cd 2、阿里云镜像加速 3、下载组件镜像 4、创建自定义网络 5、创建influxdb容器 6、创建Cadvisor 容器 7、创建granafa容器 一、部署 1、安装docker-cd [rootlocalhost ~]# iptables -F [rootlocalhost ~]# setenforce 0 setenforce: SELi…

数据结构(一):顺序表详解

在正式介绍顺序表之前&#xff0c;我们有必要先了解一个名词&#xff1a;线性表。 线性表&#xff1a; 线性表是&#xff0c;具有n个相同特性的数据元素的有限序列。常见的线性表&#xff1a;顺序表、链表、栈、队列、数组、字符串... 线性表在逻辑上是线性结构&#xff0c;但…

桥接模式来啦

桥接模式可通过组合的方式&#xff0c;将抽象和实现的部分连接起来。就实现方式来说&#xff0c;桥接模式和适配器模式有相似之处&#xff0c;但是二者应用的阶段不同。桥接模式应用于设计阶段&#xff0c;适配器模式应用于代码重构阶段。 理解桥接模式&#xff0c;其实就是理…

群晖6.X便捷的安装cpolar内网穿透

群晖6.X便捷的安装cpolar内网穿透 文章目录 群晖6.X便捷的安装cpolar内网穿透前言1. 下载cpolar的群晖套件1.1 打开群晖套件中心1.2 选择“手动安装”1.3 选择下载cpolar套件位置 2. 打开cpolar的Web-UI界面3. 注册会员 前言 随着硬件设备和软件技术的发展&#xff0c;以及数据…

最新Ubuntu LVGL SDL模拟器安装

前言 本文主要说明Ubuntu 23.4安装LVGL 9.0以及基于SDL的模拟环境。 代码下载 访问lv_port_pc_eclipse可以看到相信信息&#xff0c;官方已经打包好了整个代码环境。 安装CMAKE。 sudo apt install cmake安装SDL。 sudo apt-get update && sudo apt-get install …

③ vue组件

vue组件创建 在App.vue中添加。 技巧&#xff1a;先import&#xff0c;把vue组件地址写出来。然后在template中写名字。剩下的就自动生成。要看下import有没有多生成什么。 注意1&#xff1a; 注意2&#xff1a; 不只是能在App.vue中引入组件。任意组件中都可以引用其他组件…

SpringBoot Thymeleaf模板引擎

Thymeleaf 模板引擎 前端交给我们的页面&#xff0c;是html页面。如果是我们以前开发&#xff0c;我们需要把他们转成jsp页面&#xff0c;jsp好处就是当我们查出一些数据转发到JSP页面以后&#xff0c;我们可以用jsp轻松实现数据的显示&#xff0c;及交互等。 jsp支持非常强大…

django处理分页

当数据库量比较大的时候一定要分页查询的 在django中操作数据库进行分页 queryset models.PrettyNum.objects.all() #查询所有 queryset models.PrettyNum.objects.all()[0:10] #查询出1-10列 queryset models.PrettyNum.objects.filter(mobile__contains136)[0:10] …

关于selenium 元素定位的浅度解析

一、By类单一属性定位 元素名称 描述 Webdriver API id id属性 driver.find_element(By.ID, "id属性值") name name属性 driver.find_element(By.NAME, "name属性值") class_name class属性 driver.find_element(By.CLASS_NAME, "class_na…