硬件的效率和一致性
CPU的性能 = IPC * MHz
IPC主要是指的利用进程间的通信来提升CPU的性能(Amdahl定律,通过系统中的串行和并行的比重来提升系统西能),而MHz则是指信号交换频率(摩尔定律,处理器的晶体管数量越多,运行效率就越高)
上面的increase方法中race++成了4条字节码指令,如果在第二条和第三条执行的时候,其他的线程进行了操作,这样该线程就可能会把小的值写回主内存。当然,字节码的执行指令只有一条也不意味着是原子性操作,因为到解释器进行解释执行的时候可能需要执行多行代码实现。但现在已经能反映问题了,
我也试着进行反汇编,但是oracle的官网对一些命令包已经不支持,所以也不了了之。
第二条特性是禁止重新排序,也就是禁止代码执行顺序和代码编写顺序不一致。虚拟机或者物理机为了性能都有可能进行乱序执行的操作,比如说下面的代码
package main; import java.util.LinkedList; import java.util.List; /** * <一句话描述> * <java代码只是伪代码,因为排序问题是在反汇编之后执行的时候可能会出现问题,这里只是用java代码进行演示> * * @author lihaitao * @since 设计wiki | 需求wiki */ public class FieldResolution { String a; String b; List<String> list; boolean aBoolean = false; String c = "c"; { /** * 假设下面的赋值由线程A操作 * 是一些初始化的操作... */ a = "a"; b = "b"; list = new LinkedList<>(); aBoolean = true; String c = "c"; } { /** * 该代码块由线程B操作 */ if (aBoolean) { // do something } } } |
volatile是怎么实现的其他线程能获取到最新的值呢?在volatile修饰的变量进行assign的操作的时候,会进行一个lock指令,该指令会禁止后面的指令进行重排序,也会让该缓存写入到内存中,顺便让其他的线程中该变量的缓存失效,这样,其它线程再去读取该变量的时候需要重新的进行load,use操作。
volatile有好处,也有不好的地方,不能完全保证数据的一致性,那volatile的应用场景是什么?答案是不需要依赖数据的原值的情况,也就是我对这个变量的写操作不需要依赖于该变量的原值,我只需要赋值,其他线程再进行读取的时候就是一个正确的值。
Java内存模型的三个特性:可见性、原子性、有序性。
可见性就是一旦一个变量被修改了,其他线程能立即知道这个变量的最新值。
原子性是指的操作一次性完成,不会存在一部分完成的情况,虽然long,double是有非原子性操作协定,但是一般的虚拟机实现都会将这两个实现成原子性操作。所以我们不需要太关心。
有序性就是一个线程本身执行是有序的,但是别的线程观察该线程的时候,就会发现其执行可能是乱序的。
Java与线程
并发并不一定是线程粒度,可能是进程(PHP),java中的并发一般都是和线程挂钩的。
实现线程的3种方式和Java中的线程实现
1、利用内核线程
关于内核线程就是由操作系统内核直接支持的线程。这种线程切换由内核进行处理,操作线程调度器进行调度线程,将线程的任务映射到各个处理器上。用户的程序一般不是直接使用内核线程,而是使用实现LWP(light weight process)轻量级进程方式,轻量级进程是内核线程的一种高级接口。一般的情况是用户的程序一个进程分成多个线程,每个线程都继承LWP,每个LWP对应一个支持的KLT(kernel-level Thread),先支持内核线程,才有轻量级进程。轻量级线程和内核线程是1:1的关系。
2、利用用户线程
也就是不用内核线程进行调度维护,完完全全是用户程序去操作线程的切换调度,这样不需要内核额外的开销。看起来不错,但是用户的程序如果线程切换、创建、调度都是很麻烦的甚至不能处理,因为处理器只会向进程进行资源分配。所以java,ruby等都放弃了这个方式。
3、内核线程和用户线程共用
这种方式比较好,既有内核线程进行调度,又因为有很多用户线程执行任务,消耗内核资源较少。用户线程和LWP的比例为N:M。但是没有成为主流,不知道是实现比较难还是有其他的问题。
java中实现的方式是第一种
Java线程的调度
两种调度方式:协同式调度,抢占式调度。
java采用的是抢占式调度。
Java线程的状态
新建状态。
运行状态。
无限等待状态。wait()、join()、LockSupport.park()
有限等待状态。sleep(),wait(),join()还有LockSupport的几个method
阻塞状态。阻塞状态只出现在竞争的时候
死亡状态。挂了。
参考资料:周老师的《深入java虚拟机》