JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

「Java」伪共享验证 什么是伪共享?伪共享会导致运算结果错误吗?

wys521 2024-11-11 16:14:19 精选教程 23 ℃ 0 评论

CPU三级缓存

相对于内存IO,CPU运算速度很快;为了提升CPU计算存取速度,内存与CPU之间设置了三级缓存。三级没有理论基础,只有工业验证。为了提升读取数据效率,每次读取指定的数据块。比如 Inter 一次读取64字节,而不是一个bit一个bit读取。

三级缓存如下图所示:

伪共享问题

Java中一个 long 数字占用8字节;一个数据块最多可以同时存放4个long数字。内存中,如果同一个数据块中,存放了多个 long 数据;被多个CPU加载到了自己私有的 L1 级缓存中,同时做了修改;这样就出现了数据不一致的情况。这就是伪共享的问题。

当然,计算机使用了某些策略(硬件层面可以使用总线控制,软件层面可以使用MESI协议),使得CPU的修改对其他CPU是可见的,但这些策略肯定影响了数据的处理效率。

【Java】验证伪共享的效率

试验方法:

假设有两个long型变量x、y,使用关键字:volatile 保证其可见性。启动两个线程分别对这两个变量进行大规模赋值操作,记录操作耗时情况。

对比操作:对两个变量分别加上注解@sun.misc.Contended ,并且JVM启动参数设置添加:-XX:-RestrictContended。这样保证两个变量不在同一个数据块上,不存在伪共享问题。记录相同规模赋值操作的耗时情况。

@sun.misc.Contended
volatile long x;

@sun.misc.Contended
volatile long y;
public static void main(String[] args) throws InterruptedException {
	T05_Contended t = new T05_Contended();
	
	/**
	 * 不用注解Contended
	 * 100次均值: 205ms
	 * 
	 * 使用注解Contended
	 * 100次均值: 84ms
	 * 注意:启动JVM加入参数-XX:-RestrictContended
	 * 注解Contended才能生效
	 */
	long sum = 0;
	for(int x = 0; x < 100; x++) {
		// 创建线程
		Thread t1 = new Thread(() -> {
			for(long i = 0; i < 1_0000_0000L; i++) {
				t.x = i;
			}
		});
		Thread t2 = new Thread(() -> {
			for(long i = 0; i < 1_0000_0000L; i++) {
				t.y = i;
			}
		});
		
		// 执行赋值
		long start = System.currentTimeMillis();
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		long end = System.currentTimeMillis();
		
		// 计算总耗时
		sum = sum + (end - start);
	}
	
	System.err.println(t.x + " : " + t.y);
	System.err.println((sum / 100));
	
}

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表