持续修改中.....
一、内存结构及模型
1.1内存结构
图片来源http://blog.csdn.net/bluetjs/article/details/52874852
内存分区名称 | 用处 | 备注 |
Java 栈 | (1)存放局部变量 |
|
Java 堆 | (1)存放所有new出来的东西 (2)所有线程共享Java堆 (3)GC管理的主要区域 |
|
方法区 | (1)保存装载的类信息 (2)类型的常量池 (3)字段,方法信息 (4)方法字节码 通常和永久区(Perm)关联在一起 |
|
本地方法栈 | (1)线程私有,生命周期和线程相同 (2)栈由一系列帧组成(因此Java栈也叫做帧栈) (3)帧保存一个方法的局部变量、操作数栈、常量池指针 (4)每一次方法调用创建一个帧,并压栈 |
|
PC寄存器 | (1)每个线程拥有一个PC寄存器 (2)在线程创建时创建 (3)指向下一条指令的地址 (4)执行本地方法时,PC的值为undefined |
|
|
|
|
1.2 内存模型(JavaMemory Model,简称JMM)
本节参考 http://www.2cto.com/kf/201409/330192.html
(1) 主存保存了java程序的所有变量,当然这个变量不包括局部变量和方法参数,而工作内存则包含了这些变量的副本;
(2) 其次是线程与工作内存的关系,每个线程都有一个属于自己的工作内存,不同线程之间的工作内存是互相不可见的,且线程对变量的操作也只能是针对自己的工作内存;
(3) 最后是关于线程之间的通信机制,由于线程直接不可直接传递,假如一条线程对一个变量进行重新赋值,那么只能通过如下途径让另外一条线程知道,线程一将变量改变反应到主存中,线程二再从主存中读取,这样就基本完成了线程之间的通信了。
1.2.1内存间交互操作
① read:作用于主内存的变量,把一个变量的值从主内存传输到线程的工作内存; |
② load:作用于工作内存的变量,把read操作从主内存中得到的变量值放入工作内存的变量副本中 |
③ use:作用于工作内存的变量,把工作内存中的一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作; |
④ assign:作用于工作内存的变量,把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作; |
⑤ store:作用于工作内存的变量,把工作内存的变量传送的主内存中; |
⑥ write:作用于主内存,把store操作从工作内存中得到的变量的值放入到主内存的变量中。 |
二、静态与非静态
2.1 静(非静)态方法与成员
名称 | 定义 | 内存加载方式 | 存在位置 |
静态方法 |
|
|
|
静态成员 |
| Static随着类的加载而分配内存 | 类变量随着类的加载存在于方法区中 |
非静态方法 |
|
|
|
非静态成员 |
| 非static的成员还没有通过new创建对象而进行初始化 | 堆内存 |
方法与成员间访问控制
| 静态成员 | 静态方法 | 非静态成员 | 非静态方法 |
静态方法 | √ | √ | × | × |
非静态方法 | √ | √ | √ | √ |
注:静态方法不能访问非静态成员的原因-非static的成员是在创建对象,即new 操作的时候才初始化的;类加载的时候初始化static的成员,此时static 已经分配内存空间,所以可以访问;非static的成员还没有通过new创建对象而进行初始化,所以必然不可以访问。简言之:静态成员属于类,不需要生成对象就存在了.而非静态需要生成对象才产生,所以静态成员不能直接访问.
2.2 多线程与线程安全
多线程中使用同一个静态方法时,每个线程使用各自的实例字段 (线程工作内存),而共享一个静态字段(主内存),该实例字段是静态字段的副本。
如果该静态方法不去操作一个静态成员,只在方法内部使用实例字段(instancefield),不会引起安全性问题。但是,如果该静态方法操作了(改变)一个静态字段,则需要静态方法中采用互斥访问的方式进行安全处理。
2.3个人体会
在多线程里,针对一个非静态类来说,无论是什么方法(静态or非静态),只要不涉及到改变静态成员变量,就都是线程安全的;如果涉及到改变静态成员变量就是不安全的。说白了,就是多线程线程安全要保证数据能不干扰就不干扰,互相干扰要保证有序,可控。