更多请查看本人博客
1、Java内存模型
如下图所示,Java主要有5个存储区域,分别是方法区、堆区、Java栈、本地方法区、程序计数器。其中方法区和堆区是各个线程共有的。
1.1方法区
方法区存储了类型的各种有用信息,包括常量池、修饰符、类变量、类方法、类加载器等信息;程序在运行过程中要不断地访问方法区来获取类型信息。
1.2Java栈
程序代码、临时变量的引用都保存在Java栈中
1.3本地方法栈
本地方法栈用于线程中本地方法的调用,Java调用本地方法是非常常见的,例如Object的hashcode方法就是调用的本地方法。
1.4程序计数器
程序计数器是唯一一个不会被Java垃圾回收的地方。
1.5堆区
堆区存放了各种对象。
2、内存模型与线程安全
2.1来由
假设有一个对象x存放在堆中,线程A和线程B都会对对象x进行更改,而通常线程A和线程B都会维护一个对象x的拷贝,所有对对象x的操作都是在拷贝中进行的,那么就产生了下面两个问题:
1、不可见性
2、不一致性
不可见性的原因是当某个线程对对象x进行更改之后,其更改不会立即反馈给堆中的对象。导致这个操作对其他线程不可见。不一致性是由于多个线程对对象x同时进行了更改,这种更改先后不能够保证,导致同样的程序得到不一致的结果。解决这两个问题分别有如下的方法:
2.1、可见性
java中很多关键字都提供了可见性的支持,例如volatile,该关键字修饰的对象的更改会立即被其他线程看见。但是volatile的缺点是不具有原子性,因此使用volatile的地方要确保不会有多个线程同时对变量进行更改。
另外synchronized锁是具有可见性的,在进入synchronized代码块之前以及之后都会进行同步操作,将线程中的改动同步到堆中,使得其他的线程都能够看见。除此之外还有java.util.concurrent.lock包中的各种锁也具有同步功能。
2.2、一致性
一致性换个说法也就是原子性,也就是操作不可分割的意思。synchronized代码块以及lock,还有CAS操作都具有原子性。
2.3、不变性
最安全的线程安全方式就是不变性,不会变化的数据是不具有线程安全问题的,例如用final修饰的变量。