并发处理的广泛应用是使得Amdahl定律代替摩尔定律成为计算机性能发展源动力的根本原因,也是人类“压榨”计算机运算能力的最有力武器。

  1. Java内存模型
  2. windows 渲染java swing 界面花掉_Java

  3. Java的内存模型主要是分为主内存和工作内存,此处的划分和内存管理部分的不是一个层面的。Java的内存间交互采用lock,unlock,read,load,use,assign,store,write这八个指令完成。
    lock:作用于主内存变量,标记一个变量为线程独占状态
    unlock:同上,解除独占状态
    read:作用于主内存变量,将主内存的变量传输到线程的工作内存。
    load:作用于工作内存变量,将read传来的变量放到工作内存的变量副本中
    use:作用于工作内存的变量,将工作内存的变量的值传递给执行引擎
    assign:作用于工作内存变量,把执行引擎接收到的值赋给工作内存的变量
    store:作用于工作内存变量,将工作内存一个变量的值传递给主内存
    write:作用于主内存的变量,将store传递的变量值赋给主内存中的变量
    Java内存模型的三大特性:原子性,可见性,有序性。
  4. volatile关键字
    volatile关键字修饰的变量具有两个特征,第一是任意一个线程对该变量进行了修改,其他线程都应该立刻得知,也就是说对所有线程可见,这一特性是通过限制use,load,read三个指令和assign,store,write三个指令必须连续出现,使得每次使用volatile变量都是从主内存更新的值,每次更新后都立刻写回主内存来实现;第二是禁止指令重排序,这一特性是通过增加一个lock指令来添加内存屏障实现的。
    volatile修饰的变量不是线程安全的,应用场景是能够替换锁的常量,因为volatile效率比锁要高,但是写操作比普通变量要低。
  5. 先行发生原则(happens-before)
  1. 程序次序规则:在一个线程中,按照程序的控制流顺序依次执行
  2. 管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作
  3. volatile变量规则:volatile变量的写先行于读
  4. 线程启动规则:线程的start()方法先行与任意其他方法
  5. 线程终止规则:线程中的所有操作先行与对此线程终止的检测
  6. 线程中断规则:对线程interrupt()方法的调用先行于中断事件的发生
  7. 对象终结规则:对象的初始化先行与他的finalize()方法执行
  8. 传递性:A先行于B,B先行于C,则A先行于C
  1. Java与线程
    线程实现:使用内核线程实现;使用用户线程实现;使用用户线程加轻量级进程混合实现
    线程调度:协同式调度和抢占式调度(是通过系统自动完成调度的,JVM可以进行干预)
    线程状态:新建,运行,阻塞,等待,结束。
  2. Java中线程安全
  1. 不可变:用final修饰的变量从初始化之后就不可变,自然是线程安全的
  2. 绝对线程安全:在任何时候都不需要同步手段
  3. 相对线程安全:保证这个单独操作是线程安全的,不需要额外的同步措施,比如Vector,HashTable
  4. 线程兼容:可以通过添加额外的同步方法,使得对象能在并发条件下使用
  5. 线程对立:永远无法在并发下使用的,如Thread类的suspend()和resume()方法,会产生死锁,现在已经被废弃。
  1. Java线程安全的实现方式
  1. 互斥同步:临界区,互斥量,信号量
  2. 非阻塞同步:乐观锁,原子指令的支持TAS指令,FAI指令,Swap指令,CAS指令(存在ABA问题,可以通过增加版本号的方式解决,但是如果真的需要,不如直接采用传统的互斥策略),LL/SC指令
  3. 无同步:ThreadLocal来实现线程本地存储,来让一个变量被一个线程独享,不需要同步。每个Thread对象都有个threadLocals字段,类型ThreadLocal.ThreadLocalMap,是ThreadLoca的一个内部类,是一个map,key为Theadlocal.threadLocalHashCode,value是保存的变量(想要更加深入了解可以去读jdk源码)
  1. 锁优化
  1. 自旋锁和自适应自旋:因为挂起和恢复线程都要进入内核态,很耗费时间,所以可以让线程盲等一个循环,在尝试获取锁,避免上下文切换,故名自旋锁。而自适应自旋是对于经常自旋获得锁的线程允许多自旋几圈,否则可能取消自旋。
  2. 锁消除:JIT编译优化会把根本不会出现竞争的同步处理直接取消掉,比如StringBuffer的append。
  3. 锁粗化:虚拟机会检测到一段代码连续的加锁,可能会把锁的范围扩大,然后只加一次锁。
  4. 轻量级锁:线程在执行的时候,需要获取某个对象的锁,会在栈帧上分配一个名叫锁记录(Lock Record)的空间,用于存储对象的Mark Word的拷贝,然后使用CAS操作尝试把对象的Mark Word修改为指向当前线程的指针,如果成功了就获取了对象锁,并且把Mark Word的最后两位置为00,表示此对象处于轻量锁定状态。如果由超过两个线程来竞争锁,就会膨胀为重量级锁。
  5. 偏向锁:可以这么理解,轻量级锁是将重量级锁的互斥量开销替换为了CAS操作,而偏向锁则是直接取消了同步。当对象第一次被线程获取的时候,虚拟机将会把对象头的标志位设置为“01”表示偏向模式,同时使用CAS操作把获取锁到的线程ID记录在对象的Mark Word中,如果CAS成功,则以后进入这个锁相关的同步块都不需要进行同步,当出现另外一个线程来尝试获取锁的时候,偏向模式就失效。

这部分主要描述Java的内存模型,Java线程和并发在内存和指令级别的原理,以及JVM对于同步的处理和优化等。