线程和Java内存模型

硬件的效率和一致性

  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虚拟机》

    原文作者:TT的宝藏
    原文地址: https://www.cnblogs.com/leemumu/p/8066049.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞