来源:https://www.pdai.tech/md/java/thread/java-thread-x-theorty.html
- 
Java多线程合理利用CPU资源,平衡CPU、I/O、内存三者之间的速度差异,提高系统性能。 
- 
CPU为了平衡和内存之间的速度差异引入缓存,会导致线程间对共享变量不可见性;为了平衡I/O差异使用进程和线程,分时利用CPU,会导致非原子操作被打断原有进度;为了使缓存能够更加合理使用在编译时优化指令时指令重排问题,会导致实际运行的指令乱序。这三种情况会造成线程不安全的问题出现。为了解决这三种问题,需要保证程序在多线程运行时的可见性、原子性、指令有序性。 
- 
可见性:A线程修改共享变量之后,B立即可见。 
- 
Java提供volatile关键字保证可见性,被修饰的共享变量在修改后立即被更新到主内存中。 
- 
原子性:CPU分时复用时,A线程程序具有原子性,不被B线程中断。 
- 
Java内存模型只保证了基本读取和赋值是原子性的,其他程序可以通过synchronized和Lock来保证原子性。 
- 
有序性:指令重排后程序仍然要向按照代码先后顺序执行一样。 
- 
指令重排分为三种:编译器优化的重排序,不改变单线程程序语义情况下,重新安排语句执行顺序;指令级并行重排序,对不存在数据依赖的多条指令重叠执行;内存系统指令重排,处理器在使用缓存和读写缓冲区时的重排序。 
- 
Java源代码在经过编译器优化重排、指令级并行重排、内存系统重排之后得到最终执行的指令序列。 
- 
volatile关键字、synchronized、Lock也能保证指令有序性,禁止指令重排。 
- 
JVM规定的先行发生原则也能保证有序性(Happens-Before) 
- 
单一线程原则:单线程内在程序前面的操作先于后面;管程锁定规则:对于同一个锁,前一个线程解锁操作发生在后一个线程上锁之前;volatile变量规则:对于volatile变量,写操作在读之前;线程启动规则:start()方法在线程内所有方法之前;线程加入规则:线程B的join()返回之后,才能结束线程A;线程中断规则:interrupt()方法的调用先行于线程中断异常检测代码;对象终结规则:一个对象的初始化完成(构造函数执行结束)发生在finalize()方法之前;传递性:A在B之前,B在C之前,A也在C之前。 
- 
不可变的类型:final修饰的基本数据类型、String、枚举类型、Number的部分子类(Long\Double\BigInteger\BigDecimal) 
- 
Collections.unmodifiableXXX()方法获取不可变集合。 
- 
线程安全的实现方法:互斥同步,线程阻塞和唤醒会带来性能问题,是阻塞同步;非阻塞同步,比较并交换(Compare-and-Swap,CAS)。CAS 指令需要有 3 个操作数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操作时,只有当 V 的值等于 A,才将 V 的值更新为 B。CAS存在ABA问题。 
- 
无同步方案:不涉及共享数据的方法,不需要多线程。多个线程访问同一个方法的局部变量时,不会出现线程安全问题,因为局部变量存储在虚拟机栈中,属于线程私有的。 
- 
使用 java.lang.ThreadLocal 类来实现线程本地存储功能。在一些场景 (尤其是使用线程池) 下,由于 ThreadLocal.ThreadLocalMap 的底层数据结构导致 ThreadLocal 有内存泄漏的情况,应该尽可能在每次使用 ThreadLocal 后手动调用 remove(),以避免出现 ThreadLocal 经典的内存泄漏甚至是造成自身业务混乱的风险。 
 
                    
                 
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号