ayaov

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

来源:https://www.pdai.tech/md/java/thread/java-thread-x-theorty.html

  1. Java多线程合理利用CPU资源,平衡CPU、I/O、内存三者之间的速度差异,提高系统性能。

  2. CPU为了平衡和内存之间的速度差异引入缓存,会导致线程间对共享变量不可见性;为了平衡I/O差异使用进程和线程,分时利用CPU,会导致非原子操作被打断原有进度;为了使缓存能够更加合理使用在编译时优化指令时指令重排问题,会导致实际运行的指令乱序。这三种情况会造成线程不安全的问题出现。为了解决这三种问题,需要保证程序在多线程运行时的可见性、原子性、指令有序性。

  3. 可见性:A线程修改共享变量之后,B立即可见。

  4. Java提供volatile关键字保证可见性,被修饰的共享变量在修改后立即被更新到主内存中。

  5. 原子性:CPU分时复用时,A线程程序具有原子性,不被B线程中断。

  6. Java内存模型只保证了基本读取和赋值是原子性的,其他程序可以通过synchronized和Lock来保证原子性。

  7. 有序性:指令重排后程序仍然要向按照代码先后顺序执行一样。

  8. 指令重排分为三种:编译器优化的重排序,不改变单线程程序语义情况下,重新安排语句执行顺序;指令级并行重排序,对不存在数据依赖的多条指令重叠执行;内存系统指令重排,处理器在使用缓存和读写缓冲区时的重排序。

  9. Java源代码在经过编译器优化重排、指令级并行重排、内存系统重排之后得到最终执行的指令序列。

  10. volatile关键字、synchronized、Lock也能保证指令有序性,禁止指令重排。

  11. JVM规定的先行发生原则也能保证有序性(Happens-Before)

  12. 单一线程原则:单线程内在程序前面的操作先于后面;管程锁定规则:对于同一个锁,前一个线程解锁操作发生在后一个线程上锁之前;volatile变量规则:对于volatile变量,写操作在读之前;线程启动规则:start()方法在线程内所有方法之前;线程加入规则:线程B的join()返回之后,才能结束线程A;线程中断规则:interrupt()方法的调用先行于线程中断异常检测代码;对象终结规则:一个对象的初始化完成(构造函数执行结束)发生在finalize()方法之前;传递性:A在B之前,B在C之前,A也在C之前。

  13. 不可变的类型:final修饰的基本数据类型、String、枚举类型、Number的部分子类(Long\Double\BigInteger\BigDecimal)

  14. Collections.unmodifiableXXX()方法获取不可变集合。

  15. 线程安全的实现方法:互斥同步,线程阻塞和唤醒会带来性能问题,是阻塞同步;非阻塞同步,比较并交换(Compare-and-Swap,CAS)。CAS 指令需要有 3 个操作数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操作时,只有当 V 的值等于 A,才将 V 的值更新为 B。CAS存在ABA问题。

  16. 无同步方案:不涉及共享数据的方法,不需要多线程。多个线程访问同一个方法的局部变量时,不会出现线程安全问题,因为局部变量存储在虚拟机栈中,属于线程私有的。

  17. 使用 java.lang.ThreadLocal 类来实现线程本地存储功能。在一些场景 (尤其是使用线程池) 下,由于 ThreadLocal.ThreadLocalMap 的底层数据结构导致 ThreadLocal 有内存泄漏的情况,应该尽可能在每次使用 ThreadLocal 后手动调用 remove(),以避免出现 ThreadLocal 经典的内存泄漏甚至是造成自身业务混乱的风险。

posted on 2021-10-20 20:01  ayaov  阅读(65)  评论(0)    收藏  举报