JVM-6.Java线程内存模型和线程实现

目录:

JVM-1.自动内存管理

JVM-2.字节码和字节码指令

JVM-3.类的加载机制

JVM-4.字节码执行和方法调用

JVM-5.程序编译与代码优化

JVM-6.Java线程内存模型和线程实现

java线程内存模型

  • 主内存和工作内存
    Java的线程内存包括主内存和工作内存。
    这里的内存模型和jvm的内存区域并没有直接的关系,如果非要对应起来,那么主内存对应的是java堆,工作内存对应的是虚拟机栈。
    工作内存是每个线程私有的,而主内存则是共享的。
    线程并不直接操作主内存的数据,而是先操作工作内存里保存的副本,然后再同步到主内存中。
    JVM-6.Java线程内存模型和线程实现_第1张图片

  • volatile的特殊规则
    volatile有两个作用

    • 可见性:所谓可见性就是当一个线程修改了变量后,其他的线程都可以立刻观察到。那普通的变量就不能立刻观察到吗?是的,因为线程都是直接操作工作内存,如果只是读取,那么只是读取工作内存中的副本,并不会察觉到主内存发生了变化。
      而volatile的作用就是值发生变化时,其他线程的工作内存中的副本全部失效,必须重新从主内存获取。这样就能够获取到最新的数值了,也就实现了其他线程可以立刻观察到数据发生了变化。不过这种实现也导致了对数据进行修改时,效率要更低,因为必须通知其他线程,该变量已经被修改。
    • 禁止指令重复排序
      我们以为代码会按照我们编写的顺序去运行,但是实际上并不是这样的。为了优化,虚拟机会把一些逻辑无关的代码重新排序,以加快运行速度,这在单线程的环境下是没有问题的,但是在多线程的环境下却可能发生问题,而被标记了volatile的变量,相关的代码就不会被重排序了。
boolean initialized=falsepublic pre(){
    readConfig();//这里是一个读取配置的方法
    initialized = true;//当读取配置完成时,设置为true
    
}
public foo(){
    while(!initialized){
        //当未初始化完成时,休眠
        Thread.sleep();
    }
    //做其他事情
}

上面的代码中,pre()和foo()分别在两个线程中运行,foo只有等待初始化后才会继续执行,但是pre中的代码,从单线程的角度看,以下的两句代码并没有逻辑关系。

readConfig();
initialized = true;

所以有可能会将其顺序倒置未以下代码,那么就会出现问题,foo()函数的判断就是错误的。

//修改顺序后代码
initialized = true;
readConfig();

所以应该用 volatile 修饰initialized,这样子他的顺序就不会重排了。

volatile boolean initialized=false
  • 先行发生原则
    先行发生原则就是有一些代码必须按照顺序执行。
    1.传递性
    如果代码之间是由关系的,那么他们的顺序是不能调整的,必须按照代码顺序执行,如下面的代码。
int a = 0;
int b = a;
int c = b;

2.使用volitile关键子修饰
3.程序次序,如if语句等,必须先执行if判断
4.线程的启动,终止,中断,必须按照顺序。没有启动前当然不能终止了。
5.对象终结:只有对象初始化完成才可以终结。

线程的实现

  • 线程的实现
    线程的实现有两种方案,其一是一对一映射到内核线程,使用内核提供的轻量级线程。这样做的好处是线程的切换,多处理器的同步等复杂问题都由内核去实现。缺点是操作线程必须调用内核进行操作,需要在用户态和内核态之间进行切换。其次,每个线程都对应一个内核线程,需要耗费更多的资源。
    其二是直接使用用户线程来实现多线程,即在一个线程中模拟多个线程。优点是不需要和内核进行交互,效率高,缺点是复杂度高,必须自己实现诸如“阻塞如何处理”,“多处理器映射”等问题。
    java1.2之后的版本,使用的是第一种方案。

  • 线程调度
    我们都知道CPU是以时间片的方式来运行线程的,即每一个线程运行一个时间片后,便让出CPU资源给另外一个线程使用,达到一个貌似同时运行的效果,这种线程调度方式称之为抢占式调度。由于java的线程实际上是由内核控制的,所以java的线程调度也是抢占式调度。
    虽然java代码不能对线程的调度进行影响,但是可以影响其被调度的概率。即设置线程的优先级。前调下优先级并不能保证线程的运行顺序,只能保证优先级高的线程会有更大的几率被运行。而且由于不同平台内核优先级并不一致,所以使用优先级并不靠谱。

  • 状态转换

    • new:新建线程
    • runable:包含ready,runing。由于线程会一直在ready和runing直接切换,所以并不区分
    • watting:使用object.wait(),Thread.join(),和LockSuport.park()方法等
    • Time Waiting,使用Thread.sleep()和Object.wait(设置时间)
    • Blocked:获取锁失败后
    • Terminated:线程终止
      JVM-6.Java线程内存模型和线程实现_第2张图片

你可能感兴趣的:(java)