Java8中@Contended和伪共享

news2024/11/27 18:41:42

Java8引入了@Contented这个新的注解来减少伪共享(False Sharing)的发生。

@sun.misc.Contended注解是被设计用来解决伪共享问题的

文章目录

      • 1.缓存行
      • 2.伪共享(False Sharing)
        • 2.1 CPU的缓存机制
      • 3.填充(Padding)
      • 4.@Contended方式
      • 4.总结

1.缓存行

CPU读取内存数据时并非一次只读一个字节,而是会读一段64字节长度的连续的内存块(chunks of memory),这些块我们称之为缓存行(Cache line)。

假设你有两个线程(Thread1和Thread2)都会修改同一个volatile变量x:

在这里插入图片描述

如果Thread1先改变x的值,然后Thread2又去读它:

在这里插入图片描述

那么x所在缓存行上的所有64个字节的值都要被重新加载,因为CPU核心间交换数据是以缓存行为最小单位的。当然Thread1和Thread2是有可能在同一个核心上运行的,但我们此处假设两个线程在不同的核心上运行。

已知long类型占8个字节,缓存行长度为64个字节,那么一个缓存行可以保存8个long型变量,我们已经有了一个long型的x,假设x所在缓存行里还有其他7个long型变量,v1到v7:

在这里插入图片描述
缓存行:

在这里插入图片描述

2.伪共享(False Sharing)

这个缓存行可以被许多线程访问。如果其中一个修改了v2,那么会导致Thread1和Thread2都会重新加载整个缓存行。你可能会疑惑为什么修改了v2会导致Thread1和Thread2重新加载该缓存行,毕竟只是修改了v2的值啊。虽然说这些修改逻辑上是互相独立的,但同一缓存行上的数据是统一维护的,一致性的粒度并非体现在单个元素上。这种不必要的数据共享就称之为“伪共享”(False Sharing)。

2.1 CPU的缓存机制

学过计算机的人都知道,CPU是计算机的大脑,所有的程序,最终都要变成CPU指令在CPU中去执行。CPU的计算速度是非常快的,但是,我们知道,程序必须存储在存储介质中,程序启动之后被加载到内存中才能执行。但是内存的读取速度和CPU的计算速度之间存在非常大的差异。那么为了解决这个计算速度之间的差异,就在CPU上增加了缓存来解决这个问题。通常情况下,CPU是三级缓存结构

在这里插入图片描述

越靠近CPU的缓存,其容量就越小,但是其速度就越快。所以实际上L1的容量是最小的,这取决于CPU的具型号。

当CPU在执行计算的时候,先去L1查找数据,然后再去L2、L3,如果都没有数据则需要到主存中去加载。走得越远运算耗费的时间就越长。所以,对于一些高CPU的计算,尽量确保数据都能在L1中,降低加载次数。

在这里插入图片描述

3.填充(Padding)

一个CPU核心在加载一个缓存行时要执行上百条指令。如果一个核心要等待另外一个核心来重新加载缓存行,那么他就必须等在那里,称之为stall(停止运转)。减少伪共享也就意味着减少了stall的发生,其中一个手段就是通过填充(Padding)数据的形式,来保证本应有可能位于同一个缓存行的两个变量,在被多线程访问时必定位于不同的缓存行。

我们定义两个变量,分别在两个线程中各自增加到10亿次

不做处理

public class FalseSharingTest {

	public static void main(String[] args) throws InterruptedException {
		testPointer(new Pointer());
	}

	private static void testPointer(Pointer pointer) throws InterruptedException{
		long start  = System.currentTimeMillis();
		Thread t1 = new Thread(() -> {
			for(int i=0;i<1000000000;i++){
				pointer.x++;
			}
		});

		Thread t2 = new Thread(() -> {
			for(int i=0;i<1000000000;i++){
				pointer.y++;
			}
		});
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println("cost ["+(System.currentTimeMillis()-start)+"] ms");
		System.out.println(pointer);
	}


	static class Pointer{
		volatile long x;
		volatile long y;

		@Override
		public String toString() {
			return "Pointer{" +
					"x=" + x +
					", y=" + y +
					'}';
		}
	}
}
cost [35885] ms
Pointer{x=1000000000, y=1000000000}

采用填充的方式

public class FalseSharingTest {

	public static void main(String[] args) throws InterruptedException {
		testPointer(new Pointer());
	}

	private static void testPointer(Pointer pointer) throws InterruptedException{
		long start  = System.currentTimeMillis();
		Thread t1 = new Thread(() -> {
			for(int i=0;i<1000000000;i++){
				pointer.x++;
			}
		});

		Thread t2 = new Thread(() -> {
			for(int i=0;i<1000000000;i++){
				pointer.y++;
			}
		});
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println("cost ["+(System.currentTimeMillis()-start)+"] ms");
		System.out.println(pointer);
	}


	static class Pointer{
		volatile long x;
		long p1, p2, p3, p4, p5, p6, p7;
		volatile long y;

		@Override
		public String toString() {
			return "Pointer{" +
					"x=" + x +
					", y=" + y +
					'}';
		}
	}
}
cost [5218] ms
Pointer{x=1000000000, y=1000000000}

性能有了大幅提升

4.@Contended方式

除了对字段进行填充之外,还有一个比较清爽的方法,那就是对需要避免陷入伪共享的字段进行注解,这个注解暗示JVM应当将字段放入不同的缓存行。通过@Contended

public class FalseSharingTest {

	//-XX:-RestrictContended
	public static void main(String[] args) throws InterruptedException {
		testPointer(new Pointer());
	}

	private static void testPointer(Pointer pointer) throws InterruptedException{
		long start  = System.currentTimeMillis();
		Thread t1 = new Thread(() -> {
			for(int i=0;i<1000000000;i++){
				pointer.x++;
			}
		});

		Thread t2 = new Thread(() -> {
			for(int i=0;i<1000000000;i++){
				pointer.y++;
			}
		});
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println("cost ["+(System.currentTimeMillis()-start)+"] ms");
		System.out.println(pointer);
	}


	static class Pointer{
		@sun.misc.Contended
		volatile long x;
		@sun.misc.Contended
		volatile long y;

		@Override
		public String toString() {
			return "Pointer{" +
					"x=" + x +
					", y=" + y +
					'}';
		}
	}
}

@Contented注解将x和y移动到远离对象头部的地方

在这里插入图片描述

cost [5230] ms
Pointer{x=1000000000, y=1000000000}

4.总结

jdk7之前是填充方式, 本质是一种空间换时间的做法。

@sun.misc.Contended注解, 通过原理对象头的方式实现达到填充方式的效果。
也是ConcurrentHashMap中为了性能提升所采取的一个优化措施。自然,这个注解会因为添加了一些无用的变量而带来了内存的浪费。

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

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

相关文章

ATTCK实战系列——红队实战(二)

网络配置 网卡&#xff1a; WEB&#xff1a; PC&#xff1a; DC&#xff1a; IPWEB10.10.10.80&#xff08;内&#xff09;/192.168.111.80&#xff08;外&#xff09;PC10.10.10.201&#xff08;内&#xff09;/192.168.111.201&#xff08;外&#xff09;DC10.10.10.10物理机…

评论字数统计案例、评论回车发布、 Tab 栏切换、验证码倒计时、显示与隐藏密码——DOM事件

目录 一、DOM事件 1. 评论字数统计案例 2. 评论回车发布 3. Tab 栏切换 4. 验证码倒计时 5. 显示与隐藏密码 一、DOM事件 1. 评论字数统计案例 该案例中的显示输入字数及最大字数模块.wrapper .total 刚开始是看不见的&#xff0c;使用的是不透明度&#xff08;opacit…

量化交易-单因子分析-alphalens

1. 数据准备 1.1 计算因子IC重要函数 def get_clean_factor_and_forward_returns(factor,prices,groupbyNone,binning_by_groupFalse,quantiles5,binsNone,periods(1, 5, 10),filter_zscore20,groupby_labelsNone,max_loss0.35,zero_awareFalse,cumulative_returnsTrue)facto…

Nginx优化与防盗链

Nginx优化与防盗链 &#x1f4d2;博客主页&#xff1a; 微笑的段嘉许博客主页 &#x1f4bb;微信公众号&#xff1a;微笑的段嘉许 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐留言&#x1f4dd; &#x1f4cc;本文由微笑的段嘉许原创&#xff01; &#x1f4c…

图文并茂详解NAT协议(含实例分析)

什么是 NAT 协议 我们的计算机要想访问互联网上的信息&#xff0c;就需要一个地址&#xff0c;而且这个地址是大家&#xff08;其他主机&#xff09;所认可的&#xff0c;是公共的&#xff0c;这个地址也叫做公有 IP 地址。 与之相对的&#xff0c;除了公有 IP 地址外&#x…

Python自动化测试框架【Allure-pytest功能特性介绍】

Python自动化测试框架【Allure-pytest功能特性介绍】 目录&#xff1a;导读 前言 生成报告 测试代码 目录结构 Allure特性 Environment Categories Fixtures and Finalizers allure.attach 总结 写在最后 前言 Allure框架是一个灵活的轻量级多语言测试报告工具&am…

【PMP考试最新解读】第七版《PMBOK》应该如何备考?(含最新资料)

PMP新版大纲加入了ACP敏捷管理的内容&#xff0c;而且还不少&#xff0c;敏捷混合题型占到了 50%&#xff0c;前不久官方也发了通知8月启用第七版《PMBOK》&#xff0c;大家都觉得考试难度提升了&#xff0c;我从新考纲考完下来&#xff0c;最开始也被折磨过一段时间&#xff0…

玩游戏用哪个牌子的蓝牙耳机最好?打游戏无延迟的蓝牙耳机

作为一名苦逼的打工人&#xff0c;从早忙到黑&#xff0c;每天最期待的事莫过于下班回到家或是周末闲暇时光&#xff0c;来两把王者或吃鸡&#xff0c;配合无线游戏耳机&#xff0c;效果直接拉满&#xff0c;下面分享几款打游戏无延迟的蓝牙耳机。 一、南卡小音舱蓝牙耳机 蓝…

孪生工厂:机械臂加工产线 HMI 监控界面

2018 年&#xff0c;世界经济论坛(WEF)携手麦肯锡公司共同倡议并正式启动了全球“灯塔工厂网络项目”(Lighthouse Network)&#xff0c;共同遴选率先应用工业革命 4.0 技术实现企业盈利和持续发展的创新者与示范者。这就使得工厂系统需要对各流水线及生产运行成本方面进行多角度…

设计模式(十五)-面向对象概念

软件设计&#xff08;十五&#xff09;-UML建模&#xff08;下&#xff09;https://blog.csdn.net/ke1ying/article/details/129152487 一、设计原则 1、单一职责&#xff1a;设计目的单一的类。 2、开放-封闭原则&#xff1a;对扩展开放&#xff0c;对修改关闭。 3、里氏替…

微服务架构中的缓存设计浅析

在微服务架构中&#xff0c;缓存中间件越来越成为不可或缺的组件&#xff0c;下面聊聊微服务环境下的缓存设计。 1、简介 缓存在应用软件架构中是提高性能最直接的方式&#xff0c;如下 假设应用程序将数据存储在Mysql中&#xff0c;众所周知Mysql会将数据存储在硬盘上以防止…

git 拉取远程分支到本地

目录&#xff1a;***&#xff01;本小作者&#xff0c;是将终端和Git的可视化插件结合使用&#xff0c;刚接触的可以自习看一下&#xff0c;内容简单&#xff0c;避免弯路&#xff01;***一&#xff0c;简单了解远程分支1&#xff0c;连接远程&#xff1a;2&#xff0c;提交&am…

SpringBoot使用validator进行参数校验

Validated、Valid和BindingResultBean Validation是Java定义的一套基于注解的数据校验规范&#xff0c;比如Null、NotNull、Pattern等&#xff0c;它们位于 javax.validation.constraints这个包下。hibernate validator是对这个规范的实现&#xff0c;并增加了一些其他校验注解…

业务流程建模标注(BPMN)详细介绍

1、基本信息摘要&#xff1a;该文章的目的是对BPMN(Business Process Modeling Notation)的概要描述和介绍。描述基本的BPMN符号&#xff0c;包括这些图元如何组合成一个业务流程图&#xff08;Business Process Diagram&#xff09;2、BPMN简介2.1概述该文章的目的是对BPMN(Bu…

pytest之fixture用法

特点及优势1、命令灵活&#xff1a;对于setup.teardown&#xff0c;可以不起这两个名字2、数据共享&#xff1a;在conftest.py配置里写的方法可以实现数据共享&#xff0c;不需要import导入&#xff0c;可以跨文件共享3、scope的层次及神奇的yield组合相当于各种setup和teardow…

MySQL —— 表的操作

文章目录1. 创建表2. 查看表结构3. 修改表3.1 向表中插入数据3.2删除表中的数据3.3 修改表的性质3.3.1 添加字段3.3.2 修改字段的长度3.3.3 删除字段3.3.4 修改字段名3.3.5 修改表名4. 删除表5. 备份表前言&#xff1a; 本文会详细的讲解&#xff0c;在MySQL中表的操作。1. 创建…

Linux基础命令-uname显示系统内核信息

前言 这个命令主要是显示系统内核的相关信息&#xff0c;一般需要查看内核信息才会使用到这个命令&#xff0c;一起来看看吧。 一 命令的介绍 uname命令来自于英文词组“Unix name”的缩写&#xff0c;其功能是用于查看系统主机名、内核及硬件架构等信息。如果不加任务参数&am…

UVM实战(张强)-- UVM中的寄存器模型

目录一.整体的设计结构图二.各个组件代码详解2.1 DUT2.2 bus_driver2.3 bus_sequencer2.4 bus_monitor2.5 bus_agent2.6 bus_transaction2.7 bus_if2.8 my_if2.9 my_transaction2.10 my_sequencer2.11 my_driver2.12 my_monitor2.13 my_agent2.14 my_scoreboard2.15 my_env2.16…

龙芯GS232(MIPS 32)架构cache管理笔记

1 mips32架构 MIPS架构是一种基于精简指令集&#xff08;Reduced Instruction Set Computer&#xff0c;RISC&#xff09;的计算机处理器架构。MIPS架构由MIPS Technologies公司在1981年开发&#xff0c;并在1984年发布了第一款MIPS处理器。 MIPS架构的特点包括&#xff1a; …

Alkyne choline,685082-61-5,炔基胆碱,炔基可通过铜催化的点击化学进行修饰和共轭

1、基础产品数据&#xff08;Basic Product Data&#xff09;&#xff1a;CAS号&#xff1a;685082-61-5中文名&#xff1a;炔胆碱&#xff0c;炔基胆碱英文名&#xff1a;Alkyne-choline &#xff0c;Alkyne choline2、详细产品数据&#xff08;Detailed Product Data&#xf…