OR博客
《Java高并发程序设计》读书小记
苗锦洲
创建于:2021-08-23 22:44:34
0
30
289
0
读书小记

读书小记

第 1 章 走入并行世界

1.1 何去何从的并行世界

1.2 必须知道的几个概念

1.2.1 同步和异步

1.2.2 并发和并行

这两个比较容易混淆,都可以表示多个任务一起执行,但侧重点不同。

并发侧重多个任务交替执行,并行侧重多个任务同时执行。

并发是一定时间内连续执行多个任务,并行是多个任务同时执行。

1.2.3 临界区

临界区用来表示公共资源或者说共享数据,可以被多个线程使用,但是每次只能有一个线程使用,其他必须等待使用结束。

1.2.4 阻塞和非阻塞

多用来形容多线程间的相互影响。

阻塞:如果占用了临界区的线程不释放资源,其他线程就必须在这个临界区等待。

非阻塞:线程之间不会互相妨碍,所有线程都会不断尝试向前执行。

1.2.5 死锁、饥饿和活锁

属于多线程的活跃性问题。

死锁:线程间互相争用资源,导致永远互相等待。

饥饿:一个或多个线程由于种种原因无法获得所需的资源,导致一直无法执行。

活锁:线程所需资源不断在线程之间跳动,导致没有一个线程可以同时拿到所有资源正常执行。

1.3 并发级别

1.3.1 阻塞

悲观

1.3.2 无饥饿

没有优先级,按顺序进入临界区执行

1.3.3 无障碍

乐观
先执行,有冲突则回滚
一致性标记:操作完成前后分别读取一致性标记,判断是否更改过,有更改重试,无更改更新标记再修改数据。

1.3.4 无锁

无锁和无障碍的不同:无锁的并行总能保证有一个线程可以正常执行,其他线程必须不断重试竞争,可能出现类似饥饿现象。

1.3.5 无等待

1.4 有关并行的两个重要定律

1.4.1 Amdahl 定律

1.4.2 Gustafson 定律

1.4.3 二者侧重点不同

A:串行化比例一定,加速比有上限
G:可被并行化比例越大,加速比就可以随着 CPU 数量线性增长

1.5 回到 Java:JMM

1.5.1 原子性

操作一旦开始,不会被中断

1.5.2 可见性

共享变量修改后,其他线程能够立即知道这个修改。

1.5.3 有序性

可能发生指令重排,但不会改变串行下的语义

1.5.4 Happen-Before 规则

判断是否可以重排

第 2 章 Java 并行程序基础

2.1 有关线程你必须知道的事

  • 线程是轻量级的进程,是程序执行的最小单位。

  • 线程切换成本比进程切换小。

  • Java 中线程 Thread 的所有状态

    public enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; }
    • 线程生命周期(注意箭头方向)
    NEW-(start)->RUNNABLE-(结束)->TERMINATED RUNNABLE<-(synchronized)->BLOCKED RUNNABLE-(wait/join)->WAITING RUNNABLE<-(notify/结束)-WAITING RUNNABLE-(wait/join)->TIMED_WAITING RUNNABLE<-(notify/结束)-TIMED_WAITING

2.2 初始线程:线程的基本操作

2.2.1 新建线程

  1. 直接 new Thread() ,需要重写 run() 方法。
  2. 或者实现 Runnable 接口,在 new Thread() 的时候作为参数传进去。
  3. 使用线程池

注意:不要使用 run() 方法开启新线程,这样只会在当前线程串行执行 run() 方法中的代码。

package top.ordinaryroad.learn.chapter._2; /** * 线程基本操作 * * @author mjz * @date 2021/8/26 */ public class HelloThread implements Runnable { public static void main(String[] args) { Thread t1 = new Thread() { @Override public void run() { System.out.println("Hello Thread t1"); } }; t1.start(); Thread t2 = new Thread(new HelloThread()); t2.start(); } @Override public void run() { System.out.println("Hello Thread t2"); } }

2.2.2 终止线程

千万不要调用 stop() 方法,这样可能会使数据发生错乱,而且不好排查问题。

可以使用一个布尔类型的变量 stopMe

volatile boolean stopMe = false;

并提供一个将 stopMe 修改为 true 的方法

public void stopMe() { this.stopMe = true; }

run() 方法体最开始判断这个变量,如果为 true 则不执行操作或者跳出循环即可。

2.2.3 线程中断

2.2.4 等待 wait 和通知 notify

2.2.5 挂起 suspend 和继续执行 resume 线程

2.2.6 等待线程结束 join 和谦让 yeild

2.3 volatile 与 Java 内存模型(JMM)

2.4 分门别类的管理:线程组

2.5 驻守后台:守护线程(Daemon)

2.6 先做重要的事:线程优先级

2.7 线程安全的概念与关键字 synchronized

2.8 程序中的幽灵:隐蔽的错误

2.8.1 无提示的错误案例

求平均值整形溢出

2.8.2 并发下的 ArrayList

2.8.3 并发下诡异的 HashMap

2.8.4 初学者常见问题:错误的加锁

Integer 是不变对象,使用 synchronized(Integer) 获取到的可能不是同一个对象的锁

评论
楼主暂时不想被别人评论哦~