-
阿姆达尔定律通过系统中并行化和串行化的比重来描述多处理器系统能获得的运算加速能力,摩尔定律则用于描述处理器晶管体数量与运行效率之间的发展关系。这两个定律的更替代表了近年来硬件发展从追求处理器频率到追求多核心并行处理的发展过程。
-
实现线程主要有3种方式:使用内核线程实现,使用用户线程实现和使用用户线程加轻量级进程混合实现。Java 线程在 JDK1.2 之前,是基于称为“绿色线程”(Green Threads)的用户线程实现,而在JDK 1.2 之后,线程模型替换为基于操作系统原生线程模型来实现。
-
线程调度指的是系统为线程分配处理器使用权的过程,主要调度方式有两种,分别是协同式线程调度和抢占式线程调度。协同式线程调度的多线程系统,线程的执行时间由线程本身来控制,线程执行完毕后,通知下一个线程执行,Lua 语言的“协同例程”就是这类实现;抢占式线程调度的多线程系统,线程的执行时间由系统来分配,线程的切换不由线程本身来实现,Java 语言就是这类实现。
-
Java 语言定义了 6 种线程状态:
- 新建(New)
- 运行(Runnable):可能正在执行,可能等待着 cpu 为它分配执行时间。
- 无限期等待(Waiting):不会被分配 cpu 执行时间,要等待被其他线程显式的唤醒(Object.wait()、Thread.join()、LockSupport.park())。
- 限期等待(Timed Waiting):不会被分配 cpu 执行时间,在一定时间后它们会被系统自动唤醒(Thread.sleep()、Object.wait(time)、Thread.join(time)、LockSupport.parkNanos()、LockSupport.parkUntil())。
- 阻塞(Blocked):程序等待进入同步区域,等待着获取到一个排他锁。
- 结束(Terminated)
-
不可变(Immutable)对象一定是线程安全的,“不可变”带来的安全性是最简单和最纯粹的。如果共享数据是一个基本数据类型,那么只要在定义时使用 final 关键字修饰它就可以保证它是不可变的;如果共享数据是一个对象,那就需要保证对象的行为不会对其产生任何影响才行,而最简单的保证方式就是把带有状态的变量都声明为 final,比如 String、枚举类、Integer/Long/Double、BigInteger/BigDecimal 等。
-
即使是单核处理器也支持多线程执行代码,cpu 通过给每个线程分配 cpu 时间片来执行任务,当前任务执行一个时间片后会切换到下一个任务,所以 cpu 通过不停的切换线程执行。
-
并发执行如果没有达到一定的数量级,速度反而会比串行执行要慢。这是因为线程有创建和上下文切换的开销。
-
如何减少线程创建和上下文切换的开销?(“vmstat 1” 的 cs 参数,查看线程切换的次数)
- 无锁并发编程。多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,比如 CAS 算法、比如将数据的ID按照 Hash 算法取模分段,不同的线程处理不同段的数据。
- 使用最少线程。避免创建不需要的线程,比如通过线程池等方式。
- 协程。在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。(java 本身并不支持协程,可以参考 Quasar)
-
如何避免死锁?
- 避免一个线程同时获取多个锁。
- 尝试使用定时锁,使用 lock.tryLock(timeout) 来替代使用内部锁机制。
- 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。
-
在Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁(也称互斥锁)状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级,这种策略的目的是为了提高获得锁和释放锁的效率。有意思的是除了偏向锁,JVM 实现锁的方式都用了循环 CAS,即当一个线程想进入同步块的时候使用循环 CAS 的方式来获取锁,当它退出同步块的时候使用循环 CAS 释放锁。

如果只有一个线程进入同步代码块,那么它首先会获得“偏向锁”;当存在线程间竞争的时候,“偏向锁”会撤销,从而使用“轻量级锁”;当线程通过自旋方式始终获取不到“轻量级锁”时(获取锁的线程执行时间过长等原因),那么“轻量级锁”会膨胀成“重量级锁”。
-
基本上所有的并发模式 在解决线程冲突问题的时候,都是采用序列化访问共享资源的方案。