线程安全的程度,依次减弱
-
不可变,将对象中带状态的变量都置为final
-
绝对线程安全,完全符合线程安全定义
-
相对线程安全,对这个对象的单独的操作是线程安全的,如Vector,HashTable等
-
线程兼容,对象本身不是线程安全的,但是可以在调用端正确地使用同步手段才能保证在并发环境下正常使用。
-
线程对立,无论调用端如何努力,都不可能实现线程安全
线程安全的实现方法
1、互斥同步
synchronized关键字会在代码块的前后分别形成monitorenter和monitorexit指令,这两个指令需要一个reference对象参数,该锁有一个计数器以实现同步,进入时将计数器+1,退出时-1,本线程可重入,其他线程需阻塞等待。synchronized的缺点是由于Java线程是映射到操作系统的,所以唤醒阻塞一个线程都需要系统帮忙,需要从用户态转到内核态,耗费很多处理器时间。
ReentrantLock对synchronized的优势:
-
等待可中断
-
公平锁:必须按照申请锁的时间顺序来一次获得锁
-
锁绑定多个条件
2、非阻塞同步
为了解决线程阻塞和唤醒所带来的性能问题,先对共享数据进行操作,如果没有竞争就成功了,否则就补偿(不断重试直到成功)
long和double变量的特殊规则
8种操作一般都是原子性的,但是对于64位的数据,内存模型允许将没有被volatile修饰的64位数据的读写操作划分为两次32位的操作进行---->非原子协定但一般我们不需要将long和double声明为volatile。
先行发生原则
-
程序次序规则
-
管程锁定规则
-
volatile变量规则
-
线程启动规则
-
线程终止规则
-
线程中断规则
-
对象终结规则
-
传递性
Java与线程
Java的Thread类大多API都是Native方法,是与平台相关的。
实现线程的三种方式
-
使用内核线程实现:内核线程即直接由操作系统内核支持的线程,由内核来完成线程切换,程序使用轻量级进程接口与内核线程一对一的关系,内核线程再经由线程调度器分派给CPU。
-
使用用户线程实现:用户线程的建立同步销毁调度完全在用户态中完成,不需切换到内核态,一对多的关系。
-
用户线程+轻量级进程:多对多的关系。
线程的调度
协同式调度
线程的执行时间由线程自己控制,执行完后再主动通知系统切换线程,可能会导致一个线程长时间地阻塞
抢占式调度
由系统分配时间,线程可以主动让出时间但是不能主动获得时间,通过设置优先级确定顺序
当一个表达式A的结果已经计算过了,且A中的所有变量都没有发生过变化,那么下一次要用到A时就不用计算了,而是直接取之前A的结果。
数组边界检查消除
方法内联
逃逸分析
逃逸的定义:一个在方法里定义的变量,作为参数传递给其他方法(方法逃逸),或者赋值给类变量(线程逃逸)。
优化方法:
栈上分配:不会逃逸的对象就不在堆上分配了,就在栈上分配,那么对象所占的空间就可以随栈帧的出栈而销毁,减少垃圾收集系统的压力。
同步消除:如果一个变量肯定不会逃逸出线程,那么关于这个变量的同步措施就可以去掉。
Java 虚拟机总结给面试的你
二.Java内存模型与线程
内存模型
说了这么多的内存模型,到底什么是内存模型呢?
特定的操作协议下,对特定的内存或高速缓存进行读写访问的过程抽象。
它的作用是定义程序中各个共享的变量的访问规则,即如何将变量写入内存和从内存中取出变量。Java内存模型有主内存与工作内存之分,所有变量存在主内存中,线程则是拥有自己的工作内存,它是主内存的副本拷贝,线程只能读写工作内存。
