基础概念

同步和异步
同步和异步通常用来形容一次方法调用。

并发与并行

临界区
用来表示一种 公共资源,或是共享数据,一旦临界区资源被某个线程占用,其他线程想要使用这个资源就必须 等待。

阻塞和非阻塞
通常用来形容多线程间的相互影响,

死锁
多个线程持有彼此需要的资源不肯释放。

饥饿
线程因为种种原因无法获得其所需的资源,导致一直无法执行。

线程 优先级 过低,其它高优先级线程一直占有资源。
某一个线程一直占用关键资源不肯释放。
活锁
多个线程秉持 谦让 的原则,主动将资源释放给对方使用,导致资源不断在多个线程之间跳动,没有一个线程能同时拿到所有资源而正常执行。

并发级别

无等待
无等待要求所有线程在有限步内完成。通过限制步骤上限来避免饥饿问题


内存模型(Java Memory Model)

为了保证并发程序争正确的执行,就有了:

原子性

原子性指一个操作是不可中断的,即使在多线程的情况下也不会被干扰。

可见性

可见性指当一个线程修改了一个共享变量的值,其它线程是否能够知道这个修改。

有序性

程序在执行时,有可能会进行指令重排,CPU 指令执行的顺序不一定和程序的顺序一致。指令重排保证 串行语义一致 (即重排后执行的指令与程序真正执行顺序的语义一致),但不保证多线程语义一致。


并行程序基础

线程创建

线程停止

使用线程的 stop() 方法可以立即停止线程,但是是在线程执行过程中停止,容易造成数据不一致的问题,不建议使用。

线程中断

使用线程的 interrupt() 方法通知线程中断,收到中断通知的线程不会立即退出,而是由线程自行决定退出时机,为了避免造成数据不一致的问题,通过 isInterrupted() 方法获取线程的中断状态。wait()sleep() 等方法被中断会抛出 InterruptedException 异常,此异常会清除线程的中断标记。

等待(wait)和通知(notify)

Object 类提供了 wait()notify() 方法,意味任何对象都能调用这两个方法, wait() 方法必须包含在 synchronized 语句块中,只有获取了目标对象的监视器,才能调用这两个方法。当在一个对象实例上调用 wait() 方法,当前线程就会进入目标对象的等待队列,此队列可能会包含多个等待目标对象的线程,当 notify() 方法被调用,就会从等待队列中随机唤醒一个线程,继续执行。当对象的 notifyAll() 方法被调用,等待队列中的所有对象都会被唤醒。

wait() 和 sleep() 的区别

挂起(suspend)和继续执行(resume)

使用线程的 suspend() 方法会使线程暂停,但是并不会释放任何锁资源,直到执行此线程的 resume() 方法,如果 resume() 方法先于 suspend() 方法执行,那么线程就无法被唤醒从而继续执行,也不会释放占有的锁资源,而从线程状态上看,居然还是 RUNNABLE,所以不建议使用挂起的方式去操作线程。

等待结束(join)

在某些时候,一个线程的输入可能会依赖其它线程的输出,这就需要通过使用被依赖线程的 join() 方法加入当前线程,等待被依赖线程执行结束。

谦让(yield)

使用线程的 yield() 方法会使线程让出 CPU,但不是暂停执行,线程还会进行 CPU 的争夺。如果一个线程不那么重要,或者对应任务的优先级非常低,或者不希望它占用过多的资源,就可以使用 yield() 方法给予其它线程更多的执行机会。

线程组

使用线程组可以对线程进行分组,从而方便管理。

ThreadGroup tg = new ThreadGroup("tg");
Thread t1 = new Thread(tg, "T1");

守护线程(daemon)

守护线程是一种特殊的线程,是系统的守护者,在后台默默完成一些系统性的服务,如垃圾回收、JIT 等。如果用户线程全部结束,那么整个应用程序也应该结束,守护线程也会退出。

Thread t = new Thread();
// 必须在执行 start() 方法之前设置为守护线程
t.setDaemon(true);
t.start();

线程优先级

Java 中的线程可以有优先级,优先级高的线程在竞争资源时会更有优势。线程优先级的范围为 1~10 ,数字越大优先级越高。

/**
 * 线程可以拥有的最小优先级。
 */
public final static int MIN_PRIORITY = 1;

/**
 *分配给线程的默认优先级。
 */
public final static int NORM_PRIORITY = 5;

/**
 * 线程可以拥有的最大优先级。
 */
public final static int MAX_PRIORITY = 10;

volatile

使用 volatile 关键字可以通知虚拟机不能随意变动、优化目标指令,被此关键字修饰的变量被修改后,应用程序范围内所有的线程都能“看到”这个改动。volatile 关键字主要作用包括:

synchronized

当多个线程同时写一个数据时,容易产生写冲突,Java 提供了用于线程间同步的关键字 synchronized ,它的作用是对同步代码进行加锁,使得每次只能有一个线程进入同步代码块,从而保证线程间的安全性,synchronized 关键字有多种用法: