参考书籍
java高并发程序设计 作者 葛一鸣、郭超
1、基本概念
1.1、同步(Synchronous)和异步(Asynchronous)
同步和异步通常用来形容方法的一次调用。同步方法调用一旦开始,调用者必须等到方法调用返回以后,才能继续后续的行为。异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以进行后续的操作,异步方法通常回来另外一个线程中去执行。真个过程,不阻碍调用者的工作。如果异步调用需要返回结果,那么这个异步调用完成时,会通知调用者。
1.2、并发和并行
- 都可以表示两个或者多个任务一起执行,但是侧重点不同。
- 并发侧重于多个任务交替执行,而过个任务之间有可能还是串行的(单核多线程)。
- 并行是真正意义的 "同时执行"。
1.3 、临界区
临界区用来表示一种公共资源或者说是共享数据,可以被多个线程使用。但是每一次,只能有一个线程去使用它,一旦临界区资源被占用,其他线程要想使用这个资源,必须等待。(一个办公室共享一个打印机)
1.4、阻塞和非阻塞
- 阻塞和非阻塞通常用来形容多线程之间的相互影响。一个线程占用了临界区资源,那么其他所有需要这个资源的线程就必须在这个临界区中进行等待。等待会导致线程挂起,造成阻塞。如果这个线程一直不释放资源,那么其他所有阻塞到这个临界区的线程都不能工作。
- 非阻塞,强调没有一个线程可以妨碍其他线程执行。
1.5、死锁、饥饿、活锁
- 都属于多线程的活跃性问题,如果出现这几个问题,那么线程已经不再活跃了,很难继续往下进行。
- 饥饿是指某个或者多个线程因为种种原因无法获取所需要的资源,导致一直无法继续执行下去(线程优先级不够,导致高优先级的线程一直抢占资源,低优先级的线程无法工作),相比死锁,如果高优先级的执行完,还是能继续执行的。饥饿锁大多因为线程的优先级不一致造成低优先级的线程饥饿。
- 活锁(两个人让路,尴尬的局面,都往左或者往右)资源不断在两个线程中跳动。
- 产生死锁的四个必要条件:
1.互斥条件:一个资源每次只能被一个进程使用。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
2、并发级别
由于临界区的存在,多线程之间的并发必须受到控制,根据并发控制级别进行分类,大致上可以分为阻塞,无饥饿,无阻碍,无锁,无等待等。
2.1、阻塞
一个线程是阻塞的,那么在其他线程释放资源之前,当前线程无法继续执行。当我们使用 synchronized,或者重入锁时,得到的就是阻塞线程。在执行代码时,都需要得到临界区的锁,如果得不到,线程就会被挂起等待,直到获取了进入临界资源的锁。
2.2、无饥饿
如果线程之间有优先级,那么线程调度的时候会倾向于优先级高的线程。对于非公平的锁来说,系统允许高优先级的线程插队,这样就造成低优先级线程饥饿。如果锁是公平的,则饥饿不会产生,要想获取资源,就需要排队。
3. JMM
java内存模型的关键技术都是围绕多线程的原子性、可见性和有序性来建立的。