JAVA内存模型(未完待续)

       最近因为小组的分享的缘故,细看了一下JAVA内存模型,猛地发现原来我一直认为应该怎么样怎么样的事情,和实时是不符合的,接下来简单的记录一下最近的感悟。

 

       在JVM 1.2之前,Java的内存模型实现总是直接操作主内存的,多线程的时候访问效率比较低下。目前的JVM,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不直接在主存中进行读写,这样提高了访问效率。但造成一个结果就是如果一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,就产生了数据的不一致。

       VM系统中存在一个主内存叫:main memory,JAVA中所有的变量都是存储在这里的,所有变量共享一块内存。每个线程有自己的工作内存,叫:working memory,用来存储各个线程从main memory中的变量的拷贝。线程的所有对变量的操作都是在working memory中完成的,完成后,修改的数据会刷到main memory中。整个结构图如下:

 

线程之间的通信也是通过于主内存交互完成的(线程本身不进行通信)

 

重排序

为什么要重排序:CPU 的主频越来越高,与 cache 的交互次数也越来越多。当 CPU 的计算速度远远超过访问 cache 时,会产生 cache wait ,过多的 cache  wait 就会造成性能瓶颈

 

解决的方式:将cache分片,即将一块 cache 划分成互不关联地多个 slots ( 逻辑存储单元,又名 Memory Bank 或 Cache Bank) , CPU 可以自行选择在多个 idle bank 中进行存取。这种 SMP 的设计,显著提高了 CPU 的并行处理能力,也回避了 cache 访问瓶颈。

 

举个例子:a=1;b=2;

理想情况下,执行的顺序应该是:

cpu0->写入a=1到cache1中//A

cpu0->写入b=2到cache2中//B

如果cache1状态为忙碌的时候,cache0则需要等待

如果做过重排序的话就可以先执行B过程,这样就不会有cache wait,性能在这块就好有很好的保证

 

数据依赖性:如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。如下面:

写后读,读后写,写后写 

这三种情况只要重排后,程序的执行结果将会改变,所有编译器是不会对这几种情况进行重排序的

说到底重排序遵循的规则就是:不管怎么排序,程序的执行结果不会改变(这正是as-if-serial的语义)

 

happen-before

语义:如果A行为发生于B行为之前,那么B行为对A行为可见

遵循的规则:

程序顺序规则:一个线程中的每个操作,happens- before 于该线程中的任意后续操作。

监视器锁规则:对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁。

volatile变量规则:对一个volatile域的写,happens- before 于任意后续对这个volatile域的读。

传递性:如果A happens- before B,且B happens- before C,那么A happens- before C。

Java中,泛型是一种强类型机制,它可以让你在编译时检查类型错误,从而提高代码的安全性和可读性。在使用泛型时,我们经常会遇到父类和子类的泛型转换问题。 首先,我们需要明确一点:子类泛型不能转换成父类泛型。这是因为Java中的泛型是不协变的。例如,如果有一个类A和它的子类B,那么List<A>和List<B>之间是不存在继承关系的。 下面我们来看一个例子: ```java public class Animal { //... } public class Dog extends Animal { //... } public class Test { public static void main(String[] args) { List<Animal> list1 = new ArrayList<>(); List<Dog> list2 = new ArrayList<>(); list1 = list2; // 编译错误 } } ``` 在这个例子中,我们定义了Animal类和它的子类Dog。然后我们定义了两个List,分别是List<Animal>和List<Dog>。如果将List<Dog>赋值给List<Animal>,会出现编译错误。这是因为List<Animal>和List<Dog>之间不存在继承关系。 那么,如果我们想要让子类泛型转换成父类泛型,应该怎么办呢?这时我们可以使用通配符来解决问题。通配符可以表示任意类型,包括父类和子类。例如,我们可以将List<Dog>赋值给List<? extends Animal>,这样就可以实现子类泛型转换成父类泛型了。 下面我们来看一个使用通配符的例子: ```java public class Animal { //... } public class Dog extends Animal { //... } public class Test { public static void main(String[] args) { List<Animal> list1 = new ArrayList<>(); List<Dog> list2 = new ArrayList<>(); list1 = list2; // 编译错误 List<? extends Animal> list3 = new ArrayList<>(); list3 = list2; // 正确 } } ``` 在这个例子中,我们定义了List<? extends Animal>来表示任意继承自Animal的类型。然后我们将List<Dog>赋值给List<? extends Animal>,这样就可以实现子类泛型转换成父类泛型了。 总结一下,Java中的泛型是不协变的,子类泛型不能转换成父类泛型。如果需要实现子类泛型转换成父类泛型,可以使用通配符来解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值