美文网首页
记《JAVA并发编程实战》

记《JAVA并发编程实战》

作者: EmilioWong | 来源:发表于2018-06-07 08:33 被阅读0次

    前言

    多线程是java是一大核心,作为一个java工程师,我以前对于多线程的理解程度不够,使用上也不足。在读《JAVA并发编程实战》后,多了一些理解,特此记下。

    对多线程的理解

    1. 多线程与单线程的对比
    • 在多核CPU的情况下,单线程只能使用一核,而多线程可使用多核。
    • 在单核CPU的情况下,如果任务是IO密集型或者需要网络请求,单线程在执行IO或者网络请求时,会将任务挂起,等待完成后才继续使用CPU,对于CPU的利用率不高,而多线程则可以在处理IO或者网络请求时,切换到另一个线程,提高对CPU的利用率。而如果是CPU密集型任务的话,多线程由于上下文切换损耗的缘故,可能对CPU的利用率不如单线程高。
    1. 多线程带来的风险
    • 安全性问题:在没有充足同步的情况下,因多线程的执行顺序是不可预测的,可能会产生奇怪的结果,而且对于一个bug可能会出现难以复现的情况。
    • 活跃性问题:安全性的定义是“永远不会发生糟糕的事情”,而活跃性则关注另一个目标,即“某件正确的事情最终会发生”,包括死锁(多个线程互相持有彼此正在等待的锁而又不释放自己的已持有的锁),饥饿(线程无法访问所需要的资源而不能继续执行,最常见的资源是CPU时钟周期,如线程的优先级使用不当,持有锁时执行一些无法结束的结构),活锁(线程不断执行相同的操作,而且总是失败)。
    • 性能问题:在多线程中不仅存在单线程相同的性能问题,还存在因使用多线程而引入的其他性能问题。

    安全性

    原子性

    不可分割的操作。如a++,实际上包含了3个操作:读a,计算a+1,结果赋值给a,因此++并不是一个原子性操作。

    竞态条件

    最常见的竞态条件就是“先检查后执行”,CAS(CompareAndSet)。在多线程中处理竞态条件需要复合操作,以复合原子性。

    锁的重入性

    “重入”指的是获取锁的最小粒度是“线程”,而不是“调用”。比如一个线程调用一个方法时,获取锁进入同步块,在这个同步块里调用另一个需要同一个锁的方法时,不会因需要这个已持有的锁而永远等待。

    对象的共享

    volatile变量

    volatile修饰的变量会在每次使用时都去内存读取最新值,而不是使用寄存器里存储的值。

    发布与逸出

    “发布”指的是使对象能在当前作用域之外被调用。逸出指的是对象被错误发布。开发时需要避免逸出。如在方法里将不想发布的对象设置进传入的参数对象里或者作为返回值返回;一些不对外的方法设置成private。

    “除非需要更高的可见性,否则应将所有的域都声明为私有域”是一个良好的编程习惯。

    ThreadLocal

    ThreadLocal是对可变的单例变量或者全局变量的一个本地副本,通过调用get方法,各线程可持有一个副本,这样这个变量对象就算不是线程安全的,也不影响各线程的运行。

    final

    final域能确保初始化过程的安全性,从而可以不受限制地访问不可变对象,并在共享这些对象时无须同步。

    “除非需要某个域是可变的,否则应将其声明为final域”是一个良好的编程习惯。

    如何避免死锁

    • 固定获取锁的顺序。System.identityHashCode方法获取锁对象的hashCode,通过比较hashCode值来决定加锁顺序。如果hashCode值一致,可以使用“加时锁”,在获取锁前先尝试获取“加时锁”,保证每次只有一个线程以未知的顺序获取锁。由于System.identityHashCode法师散列冲突的频率非常低,这项技术以最小的代价换来了最大的安全。
    • 使用Lock.tryLock定时锁,显示指定一个超时时限。

    如何估计线程池大小

    在使用线程池的时候,如果线程池过大,那么大量的线程将在相对很少的CPU和内存资源上发生竞争,这不仅会导致更高的内存使用量,而且还可能耗尽资源。如果线程池过小,那么将导致许多空闲的处理器无法执行工作,从而降低吞吐率。
    给定下列定义:
    Ncpu = number of CPUs
    Ucpu = target CPU utilization,0<=Ucpu<=1
    W/C = ratio of wait time to compute time
    要使处理器达到预期的使用率,线程池的最优大小等于
    Nthreads = NcpuUcpu(1+W/C)
    可以通过Runtime来获取CPU的数目:
    int N_CPU = Runtime.getRuntime().availableProcessors();

    相关文章

      网友评论

          本文标题:记《JAVA并发编程实战》

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