《java并发编程的艺术》笔记

减少上下文切换方法:P3
1、无锁并发编程;
2、CAS算法;
3、使用最少线程;
4、使用协程;

避免死锁的常见方法:P6
1、避免一个线程同时获取多个锁;
2、避免一个线程在锁内同时占用多个资源,尽量保证一个锁只占用一个资源;
3、尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制;
4、对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况;

处理器实现原子操作方法:P17
1、第一个机制是通过总线锁保证原子性;
2、第二个机制是通过缓存锁定来保证原子性;

java实现原子操作:P18
1、使用循环CAS实现原子操作;
2、使用锁机制实现原子性;

线程之间的通信机制:共享内存和消息传递;P21

JMM:java内存模型;P22

重排序分类:P23
1、编译器优化的重排序;
2、指令集并行的重排序;
3、内存系统的重排序;

顺序一致性内存模型特性:P32
1、一个线程中的所有操作必须按照程序的顺序来执行;
2、(不管程序是否同步)所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见;

并发编程模型分类:P24
1、顺序一致性内存模型;
2、java内存模型;

volatile变量特性:P39
1、可见性;
2、原子性;volatile++不具有原子性;

java线程之间的通信方式:P54
1、A线程写volatile变量,随后B线程读取这个volatile变量;
2、A线程写volatile变量,随后B线程用CAS更新这个volatile变量;
3、A线程用CAS更新一个volatile变量,随后B线程用CAS更新这个volatile变量;
4、A线程用CAS更新一个volatile变量,随后B线程读取这个volatile变量;

java线程状态:P87
new、runnable、blocked、waitting、time_watting、terminated;

当一个java虚拟机中不存在非Daemom线程时,java虚拟机将会退出。java虚拟机退出时Daemom线程中的finally块并不一定会执行,在构建Daemom线程时,不能依靠finally块中的内容来确保关闭或清理资源的逻辑.P91

P92:许多声明抛出InterruptedException的方法(例如Thread.sleep(long millis)方法)这些方法在抛出InterruptedException之前,java虚拟机会先将该线程的中断标识位清除,然后抛出InterruptedException。

P95:suspend()方法在调用后,线程不会释放已经占有的资源(比如锁),而是占有者资源进入睡眠状态,容易引发死锁问题。stop()方法在终结一个线程时不会保证线程的资源被正常释放,通常是没有给予线程完成资源释放工作的机会,因此会导致程序可能在不确定状态下。

安全的终止线程:P95
1、中断状态;interrupt();
2、用boolean变量控制;

等待/通知机制(P99):指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象的wait()方法返回,进而执行后续操作;

调用wait()、notify()、notifyAll()应注意:P100
1、调用wait()、notify()、notifyAll()需先对调用对象加锁;
2、调用wait()方法后,线程状态有running变为waitting,并将当前线程放到对象的等待队列中;
3、notify()或notifyAll()方法调用后,等待线程依旧不会从wait()返回,需要调用notify()或notifyAll()方法的线程释放锁后,等待线程才有机会从wait()返回;
4、notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而notifyAll()则是将等待队列中的所有等待线程从等待队列中移到同步队列,被移动的线程状态由waitting编程blocked;
5、从wait()方法返回的前提是获得了调用对象的锁。

管道输入/输出流:P102
1、主要用于线程之间的数据传输,传输的媒介为内存;
2、实现方式:PipedOutputStream、PipedInputStream、pipedReader、PipedWriter,前两种面向字节,后两种面向字符;

Thread.join():P103
1、含义:当前线程A等待thread线程终止之后才从thread.join()返回;
2、

线程池(P114)本质:使用了一个线程安全的工作队列连接工作者线程和客户端线程,客户端线程将任务放入工作队列后便返回,而工作者线程则不断地从工作队列上取出工作并执行。当工作队列为空时,所有的工作者线程均等待在工作队列上,当有客户端线程提交了一个任务之后会通知任意一个工作者线程,随着大量的任务被提交,更多的工作者线程会被唤醒。

Lock接口:P120
特性:P121
1、尝试非阻塞的获取锁;当前线程尝试获取锁,如果这一时刻锁没有被其他线程获取到,这成功获取并持有锁;
2、能被中断的获取锁;与synchronized不同,获取到锁的线程能够响应中断,当获取到锁的线程被中断时,中断异常会被抛出,同时锁会被释放。
3、超时获取锁;在指定的截止时间之前或许锁,如果截止时间到了仍旧无法获取锁,则返回。

队列同步器(AbstractQueuedSynchronizer)P121;

同步器提供的模板方法分类:P122
1、独占似获取和释放同步状态;
2、共享式获取和释放同步状态;
3、查询同步队列中的等待线程情况;

Java原子操作类:java.util.concurrent.atomic包(简称Atomic包)P182
1、原子更新类型:P182
1)原子更新基本类 P182;
a)AtomicBoolean:原子更新布尔类型;
b)AtomicInteger:原子更新整型;
c)AtomicLong:原子更新长整型;
2)原子更新数组 P184;
a)AtomicIntegerArray:原子更新整型数组里的元素;
b)AtomicLongArray:原子更新长整形数组里的元素;
c)AtomicReferenceArray:原子更新引用类型数组里的元素;
3)原子更新引用 P185;
a)AtomicReference:原子更新引用类型;
b)AtomicRefererceFieldUqdate:原子更新引用类型里的字段;
c)AtomicMarkableReference:原子更新带有标志位的引用类型;
4)原子更新属性P187;
a)AtomicIntegerFieldUqdat:原子更新整形的字段的更新器;
b)AtomicLongFieldUqdat:原子更新长整形的字段的更新器;
c)AtomicStampedReference:原子更新带有版本号的引用类型;

读写锁:ReentrantReadWriteLock
1、读写锁维护了一对锁,包含一个读锁和一个写锁;P140
2、写锁是一个支持重入的排它锁;P143;
3、读锁是一个支持重进入的共享锁;P144
4、锁降级:指的是写锁降级成读锁,指把持住(当前拥有的)写锁,在获取到读锁,随后释放写锁(之前持有有的)的过程。P145

LockSupport :P146
1、LockSupport定义了一组一park开头的方法用来阻塞线当前线程,以及unpark(Thread thread)方法来唤醒一个被阻塞的线程;

对象监视器方法:P147
wait()、wait(long timeout)、notify()、notifyAll();

Condition:P148
1、Condition依赖于Lock对象;
2、在调用方法前先获取锁:lock.lock();
3、Lock(同步器)拥有一个同步队列和多个等待队列;
4、Condition的实现是同步器的内部类,因此每个condition实例都能访问同步器提供的方法,相当于每个condition都拥有所属同步器的引用;

ConcurrentHashMap:P155 线程安全且高效的HashMap
1、并发情况下不使用HashMap原因P155:在多线程环境下,使用HashMap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap。HashMap在并发执行put操作时会引起死循环,是因为多线程会导致HashMap的Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,就会产生死循环获取Entry;
2、HashTable P156:HashTable使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下;
3、ConcurrentHashMap P156:用锁分段技术提高效率;
1)ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成;
2)Segment:可重入锁;HashEntry:用于存储键值对数据;
ConcurrentLinkQueue:P161 线程安全队列
1、ConcurrentLinkQueue是一个基于链接节点的无界线程安全队列,采用先进先出的规则对节点进行排序,采用“wait-free”算法(即CAS算法)来实现,该算法在Michel&Scott算法上进行了一些修改;
2、入队列 P162:入队列就是将入队节点添加到队列的尾部。
入队列主要做两件事情:
1)将入队节点设置成当前队列尾节点的下一个节点;
2)更新tail节点,如果tail节点的next节点不为空,则将入队节点设置成tail节点,如果tail节点的next节点为空,则将入队节点设置成tail的next节点,所以tail节点不总是尾节点;
3、出队列 P165:从队列里返回一个节点元素,并清空该节点对元素的引用;
阻塞队列 P167
1、阻塞队列是一个支持两个附加操作的队列。两个附加的操作支持阻塞的插入和移除方法;
2、Java里的7个阻塞队列:P168
1)ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列;先进先出原则;
2)LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列;先进先出原则;
3)PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列;支持延时获取元素,
4)DelayQueue:一个使用优先级队列实现的无界阻塞队列;
5)SynchronousQueue:一个不存储元素的阻塞队列;
6 ) LinkedTransferQueue:一个由链表结构组成的无界阻塞队列;
7)LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列;
Fork/Join框架 P175:是java7提供的一个用于执行并行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。
1、工作窃取算法 P176:指某个线程从其他队列里窃取任务来执行,通常使用双端队列,被窃取任务线程永远从双端队列的头部拿任务执行,窃取任务的线程永远从双端队列的尾部拿任务执行;
2、ForkJoinPool由PorkJoinTask数组和ForkJoinThread数组组成,ForkJoinTask数组负责将存放程序提交给ForkJoinPool的任务,而ForkJoinThread数组负责执行这些任务;P179
3、

Java中的并发工具类:
1、CountdownLatch P189:CountdownLatch允许一个或多个线程等待其他线程完成操作;
2、CyclicBarrier P191:字面意思是可循环使用(Cyclic)的屏障(Barrier),它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的才会继续运行;
3、CountdownLatch和CyclicBarrier的区别 P195:CountdownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置。所以CyclicBarrier能处理更为复杂的业务场景;
4、Semaphore P196:Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源;
5、Exchanger 线程间交换数据工具 P198:Exchanger 是一个用于线程间协作的工具类,用于线程间的数据交换;
1)线程通过exchange方法交换数据,一个线程先执行exchange,它会一直等到第二个线程也执行exchange;
线程池:

Executor框架
1、任务。P209 包括Runnable接口或Callable接口;
2、任务的执行。包括任务执行机制的核心几口Executor,以及继承自Executor的ExecutorService接口。两个关键类实现了ExecutorService接口(ThreadPoolExecutor和ScheduledThreadPoolExecutor);
3、异步计算的结果。 包括接口Future和实现Future接口的FutureTask类;

Executor框架成员:P211 ThreadPoolExecutor、ScheduledThreadPoolExecutor、Future接口、Runnable接口、Callable接口和Executors。
1、ThreadPoolExecutor:P211 ThreadPoolExecutor通常使用工厂类Executors来创建;
1)FixedThreadPool:使用线程数固定。适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场景,适用于负载比较重的服务器。
public static Executorservice newFixedThreadPool(int nThreads);
public static Executorservice newFixedThreadPool(int nThreads,ThreadFactory threadFactory);
2)SingleThreadExecutor:使用单个线程。适用于要保证顺序的执行各个任务,并且在任意时间点,不会有多个线程是活动的场景。
public static Executorservice newSingleThreadExecutor();
public static Executorservice newSingleThreadExecutor(ThreadFactory threadFactory);
3)CacheThreadPool:根据需要创建新线程。CacheThreadPool是大小无界的线程池,适用于执行很多的短期异步任务的小程序,或者是附在比较轻的服务器。
public static Executorservice newCacheThreadPool();
public static Executorservice newCacheThreadPool(ThreadFactory threadFactory);
2、ScheduledThreadPoolExecutor:P211 ScheduledThreadPoolExecutor通常使用工厂类Executors来创建;
1)ScheduledThreadPoolExecutor:创建线程数固定。适用于需要多个后台执行周期任务,同时为了满足资源管理的需求而限制后台线程的数量的用用户场景。
public static ScheduledExecutorSevice newScheduledThreadPoolExecutor(int corePoolSize);
public static ScheduledExecutorSevice newScheduledThreadPoolExecutor(int corePoolSize,ThreadFactory threadFactory);
2)SingleThreadScheduledExecutor:创建线程数固定。适用于需要单个后台线程执行周期任务,同时需要保证顺序的执行各个任务的应用和场景。
public static ScheduledExecutorSevice newSingleThreadScheduledExecutor();
public static ScheduledExecutorSevice newSingleThreadScheduledExecutor(ThreadFactory threadFactory);
3、Future接口:P222 Future接口和实现Future接口的FutureTask类用来表示异步计算的结果。
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable<T> task,T result);
Future<T> submit(Runnable task);
4、Runnable接口和Callable接口:P222 Runnable接口和Callable接口的实现类,都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行。它们之间的区别是Runnable不会返回结果,而Callable可以返回结果。可以使用工厂类Executors来把一个Runnable接口包装成一个Callable。
public ststic Callable<Object> callable(Runnable task);
public ststic <T> Callable<T> callable(Runnable task,T result);

 

点赞