美文网首页
java高并发总结

java高并发总结

作者: Easy的幸福 | 来源:发表于2019-10-15 10:26 被阅读0次
    出现并发问题的三个源头:
    • 缓存导致多线程之间的可见性问题
    • 线程之间切换带来的原子性问题

    时间片:操作系统允许某个线程执行一小段时间,例如50毫秒,过了50毫秒操作系统就会重新选择一个线程来执行(我们称为“任务切换”),这个50毫秒称为“时间片”。


    image

    任务切换的时机大多数是在时间片结束的时候,我们现在基本都使用高级语言编程,高级语言里一条语句往往需要多条CPU指令完成,例如上面代码中的count += 1,至少需要三条CPU指令。

    指令1:首先,需要把变量count从内存加载到CPU的寄存器;
    指令2:之后,在寄存器中执行+1操作;
    指令3:最后,将结果写入内存(缓存机制导致可能写入的是CPU缓存而不是内存)。
    

    操作系统做任务切换,可以发生在任何一条CPU指令执行完,是的,是CPU指令,而不是高级语言里的一条语句。

    • 编译优化带来的有序性问题
    public class Singleton {
      static Singleton instance;
      static Singleton getInstance(){
        if (instance == null) {
          synchronized(Singleton.class) {
            if (instance == null)
              instance = new Singleton();
            }
        }
        return instance;
      }
    }
    

    这看上去一切都很完美,无懈可击,但实际上这个getInstance()方法并不完美。问题出在哪里呢?出在new操作上,我们以为的new操作应该是:

    1、分配一块内存M;
    2、在内存M上初始化Singleton对象;
    3、然后M的地址赋值给instance变量。
    

    但是实际上优化后的执行路径却是这样的:

    1、分配一块内存M;
    2、将M的地址赋值给instance变量;
    3、最后在内存M上初始化Singleton对象。
    
    image

    当然可以对instance进行volatile语义声明,就可以禁止指令重排序,避免该情况发生.但是这样又会出现volatile的写入屏障问题,也就是所谓happen-before问题。

    什么是Java内存模型?Java如何解决可见性和有序性问题?

    Java内存模型是个很复杂的规范,Java内存模型规范了JVM如何提供按需禁用缓存和编译优化的方法。具体来说,这些方法包括 volatile、synchronized 和 final 三个关键字,以及八项 Happens-Before 规则。

    Happens-Before 规则
    很多网文也都爱按字面意思翻译成“先行发生”,
    它真正要表达的是:前面一个操作的结果对后续操作是可见的。
    
    //示例参考代码
    class VolatileExample {
      int x = 0;
      volatile boolean v = false;
      public void writer() {
        x = 42;
        v = true;
      }
      public void reader() {
        if (v == true) {
          // 这里x会是多少呢?
        }
      }
    }
    
    • 程序的顺序性规则
    • volatile变量规则
    • 传递性

    这条规则是指如果A Happens-Before B,且B Happens-Before C,那么A Happens-Before C


    image
    • 管程中锁的规则

    这条规则是指对一个锁的解锁 Happens-Before 于后续对这个锁的加锁。
    管程是一种通用的同步原语,在Java中指的就是synchronized,synchronized是Java里对管程的实现。管程中的锁在Java里是隐式实现的.如下:

    synchronized (this) { //此处自动加锁
      // x是共享变量,初始值=10
      if (this.x < 12) {
        this.x = 12; 
      }  
    } //此处自动解锁
    
    • 线程 start() 规则

    它是指主线程A启动子线程B后,子线程B能够看到主线程在启动子线程B前的操作。
    换句话说就是,如果线程A调用线程B的 start() 方法(即在线程A中启动线程B),那么该start()操作 Happens-Before 于线程B中的任意操作。

    • 线程 join() 规则

    如果在线程A中,调用线程B的 join() 并成功返回,那么线程B中的任意操作Happens-Before 于该 join() 操作的返回,线程A都能看到B线程的任意操作。

    • 线程中断规则

    对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生

    • 对象终结规则:

    一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。

    Java内存模型底层怎么实现的?

    主要是通过内存屏障(memory barrier)禁止重排序的,即时编译器根据具体的底层体系架构,将这些内存屏障替换成具体的 CPU 指令。对于编译器而言,内存屏障将限制它所能做的重排序优化。而对于处理器而言,内存屏障将会导致缓存的刷新操作。比如,对于volatile,编译器将在volatile字段的读写操作前后各插入一些内存屏障。 [

    相关文章

      网友评论

          本文标题:java高并发总结

          本文链接:https://www.haomeiwen.com/subject/jibjmctx.html