6.JUC
Java · 43 篇笔记
- 6.1 先谈硬件historical
要理解JMM,我们先要理解底层硬件的工作原理 1. 冯诺依曼体系结构 冯诺依曼提出将程序当作数据对待,将程序(指令)和数据用同样的方式储存。根据这个理论计算机被分成控制器、运算器、存储器、输出设备、输入设备这几个部件,如下图 其中运算器和控制器组合成了CPU,CPU执行指令或者操作数据的时候都要跟存
- 6.2 2.Synchronizedhistorical
1. 是什么 Java 中悲观锁的一种实现,相比于 volatile 是重量级锁,可以保证原子性、有序性、可见性 - 重量级 会引起上下文切换(会造成线程阻塞) - 原子性 synchronized 方法、synchronized 代码块被视作原子的 - 有序性 线程 A 对于锁 X 的释放发生于线
- 6.3 3.volatilehistorical
1. 是什么 Java 的轻量级锁,主要保证了有序性、可见性和一定的原子性 - 轻量级 相比于 synchronized,volatile 不会引起上下文切换(不会造成线程阻塞) - 原子性 对任意单个 volatile 变量的读/写具有原子性,但类似于 volatile++这种复合操作不具有原子性
- 6.4 4.CAShistorical
1. 是什么 要理解CAS,我们首先得了解乐观锁和悲观锁的概念。 1.1. 乐观锁与悲观锁 悲观锁:假设每次操作数据的时候总有人一起操作数据。因此我操作数据前先上锁,直到我操作完释放锁,别人都只能阻塞等待。 乐观锁:假设每次操作数据的时候没人跟我一起操作数据。因此我只在更新的时候检查一下有没有其他人
- 6.5 5.AQShistorical
1. 是什么 队列同步器,用于实现JUC包的其他并发工具类 2. 如何使用 一般我们不直接使用AQS,而是使用JUC中的其他工具类(如CountDownLatch等),这些工具类覆盖了几乎所有的使用场景,只有在这些工具类无法满足我们的需求时,才去用AQS实现自己的并发工具。 实现的一般的套路如下:
- 6.6 6.ReentrantLockhistorical
1. 是什么 在jdk5之前,synchronized效率极低,于是写了ReentranLock代替。 后来jdk7优化了synchronized,参考锁的优化.md。两者性能区别不大 1.1. synchronized vs ReentranLock 比较 Synchronized Reentra
- 6.7 7.Lock之Conditionhistorical
1. 是什么 类似object的wait和notify方法配合synchronized使用 condition的await和notify方法配合Lock使用,用来实现条件等待与唤醒 2. 如何使用 - 生产者消费者模式 3. 实现原理 3.1. uml 3.2. 创建Condition对象 - ne
- 6.8 8.CyclicBarrierhistorical
1. 是什么 可重复使用的计数器,让一堆线程互相等待,条件满足时一起往下执行 底层使用Lock+Condition实现阻塞等待和唤醒 2. 如何使用 2.1. 不带Runnable 当所有线程都到达await点的时候才一起往下执行 2.2. 带Runnale 当所有线程都到达await点的时候,最后
- 6.9 9.CountDownLatchhistorical
1. 是什么 不能重复使用的计数器。让一个线程等待其他线程完事再往下执行,类似于Thread.join() 底层使用AQS实现 2. 如何使用 - 注意 这里countdown的线程不会互相等待,谁先执行完谁就先退出 2.1. CountDownLatch VS CyclicBarrier Coun
- 6.10 10.CopyOnWriteArrayListhistorical
1. 是什么 这个list借鉴的是读写分离的思想(弱一致性) - 读的时候可以并发读,不加锁; - 写的时候需要加锁,复制一份原有数据进行修改,改完后写回list 2. 如何使用 3. 原理 3.1. 构造方法 - 可以看到底层是通过object数组实现, - 通过 getArray/setArra
- 6.11 11.CopyOnWriteArraySethistorical
1. 是什么 写时复制的set,有序不重复,底层使用CopyOnWriteArrayList实现 2. 如何使用 3. 原理分析 3.1. 构造方法 3.1.1. 底层使用CopyOnWriteArrayList实现 3.2. add方法 3.2.1. 转调CopyOnWriteArrayList
- 6.12 ArrayBlockingQueuehistorical
1. 是什么 使用Object数组实现的有界的阻塞队列 读读、读写、写写相互阻塞 2. 如何使用 2.1. 方法选择 方法\处理方式 抛出异常 返回特殊值 一直阻塞 超时退出 ------------ --------- --------- ------- ------------------ 插入
- 6.13 13.ThreadLocalhistorical
1. 是什么 不是线程同步机制,是一种线程数据隔离机制。 多线程共享变量通信的情况下,我们需要保证线程安全。一种方法是使用锁,另一种就是数据隔离机制。 ThreadLocal用的是后一种,即每个线程操作的是自己独有的数据,因此互相之间不会影响 2. 如何使用 3. 原理分析 3.1. uml Thr
- 6.14 14.ThreadPoolhistorical
1. 是什么 Java的线程池框架,他提供了“任务提交”与“任务执行”分离开的机制 1.1. 为什么需要线程池 用来复用线程 - 第一,线程的创建和销毁开销比较大 - 第二,线程数量过多的话会导致cpu忙于上下文切换而不“干活” 1.2. 使用场景 - 单个任务执行的时间不能太长 - 任务数很多 2
- 6.15 CompletableFuturehistorical
1. 是什么 用于异步编程。(准备说是非阻塞) Java中所谓的异步编程其实就是把阻塞的代码放在一个单独的线程中执行,并且有结果时会通知主线程 2. Future VS CompletableFutre Future CompletableFutre ------------------ -----
- 6.16 JDK1.7的ConcurrentHashMaphistorical
1. 构造方法 2. put方法 2.1. hash 2.2. ensureSegment 2.3. segment的put方法 2.3.1. scanAndLockForPut 2.3.2. rehash 3. get 4. containsKey方法 5. remove 5.1. segment
- 6.17 fork_joinhistorical
1. 是什么 并行执行的框架。把一个大任务分成多个小任务,每个小任务计算结果,最后汇总每个小任务的结果得到大任务的结果 1.1. 为什么出现 简单地使用线程池实现fork join需要考虑当前线程也跟着干活,而不是变成监工 2. 使用场景 计算密集型的任务 3. 如何使用 4. 原理分析 4.1.
- 6.18 Exchangerhistorical
1. 是什么 用于两个线程之间交换数据,数据的流向是双向的。即如果有Thread1和Thread2两个线程,Thread1传给Thread2一个数据 ,Thread2同时也会传给Thread1一个数据。 1.1. Exchanger对比SychronousQueue Exchanger Sychro
- 6.19 ReentrantReadWriteLockhistorical
1. ReentrantReadWriteLock是什么 ReentrantLock保证了同一时间只有一个线程可以在临界区读或者写数据,这意味着如果有两个读线程同时读取数据,ReentrantLock也只允许其中一个通过,但我们想要的是读可以并发执行,一旦有写则其他线程等待。如下表: 是否可以同时进
- 6.20 Semaphorehistorical
1. 是什么 限流工具类,同一时间只允许n个线程访问某资源 2. 原理分析 2.1. uml 可以看出Semaphore也有公平的和非公平之分,参考 - 非公平信号量.md - 公平信号量.md
- 6.21 Kernel_level_threadhistorical
1. 是什么 User level thread是操作系统感知不到的,由应用程序自己创建的线程并负责调度 Kernel level thread是操作系统能感知到的,并由操作系统负责调度 2. 如何验证Java线程是kernel级别的 Kernel level thread. Java程序通过JVM
- 6.22 Unsafehistorical
1. 是什么 Unsafe类顾名思义是个不安全的类,为什么说他不安全呢?我们得从C语言的内存管理说起。 1.1. C语言的内存管理 C语言中每个变量在内存中都有一个地址,我们可以用 &变量名 获取这个地址,然后用指针变量进行接收。说白了指针保存的就是内存地址。 我们要使用一段内存的时候需要用 mal
- 6.23 再谈JMMhistorical
了解了计算机底层知识后,再来看JMM就容易多了 1. 为什么需要JMM 之前提到过内存模型规定了程序的内存操作(读操作和写操作)所有可能的执行顺序中哪些是正确的,而不同的处理器架构有不同的内存模型,Java作为一个跨平台(OS和硬件)的语言,为了屏蔽底层的这些差异,定义了自己的内存模型:JMM 2.
- 6.24 锁的优化historical
1. JVM对锁的优化 JDK1.5之前sychronized内部锁的效率很低,1.5之后做了大量优化,提升了锁的性能。 优化措施主要有以下几个: 1.1. 锁的消除 JIT(不是javac)借助逃逸分析以及内联技术,分析某个同步代码块是否只能被一个线程访问, 是的话则不生成monitor相关的字节
- 6.25 手写AQShistorical
我们可以自己动手写一个简单的AQS,以更好地理解AQS实际的源码 1. 需求 1. 锁是排他的,一旦这个锁被某个线程占有,只要这个锁没被释放,他就不能被其他线程占有。因此需要保存当前占有锁的线程 2. 要有一个单独的字段表示当前锁的状态,是空闲还是已被占有 3. 同一时间有很多线程抢占锁,只有一个线
- 6.26 公平锁historical
所谓公平锁,遵循先到先得的原则。 即使锁已经被释放了,后到的也不能去抢占锁,得等到前面没人时才能去获取 1. 如何使用 2. 原理分析 2.1. 构造方法 2.1.1. 底层使用AQS实现 2.2. 加锁 - lock 2.2.1. 调用公平锁的lock方法 - FairSync.lock 2.2.
- 6.27 BlockingQueuehistorical
1. 是什么 线程安全的阻塞队列。 特点: - 先进先出: 既然是队列那肯定是先进先出 - 阻塞 支持在插入元素时,如果队列已满,那么阻塞,等待队列非满 也支持在删除元素时,如果队列为空,那么阻塞,等待队列非空 - 无界有界 数组容量的大小。无界其实是Integer.MAX VALUE - 线程安全
- 6.28 Executorshistorical
1. 使用 2. newCachedThreadPool 3. newScheduledThreadPool 4. newFixedThreadPool 5. newSingleThreadExecutor
- 6.29 JDK1.8的ConcurrentHashMaphistorical
1. 是什么 线程安全的HashMap,底层使用sychronized+CAS+HashMap的结构(数组+链表+红黑树)实现 2. 如何使用 3. 原理分析 3.1. 构造方法 3.1.1. Node 3.2. put方法【有加锁】 - putVal - 4行:计算key的hash,这里不是简单得
- 6.30 生产者消费者historical
1. 使用BlockingQueue 2. 使用wait notify 3. 使用Lock Condition 相比于上面的wait notify这里用了两个condition,这样子唤醒的时候就不会把生产者和消费者一起唤醒,只唤醒某一个即可(即生产者唤醒消费者,消费者唤醒生产者)
- 6.31 非公平ReadWriteLockhistorical
1. 是什么 无论队列前面是否有人排队等待锁,我直接去抢 2. 怎么使用 3. 源码分析 3.1. uml 3.2. 构造方法 - ReentrantReadWriteLock - ReentrantReadWriteLock.ReadLock - ReentrantReadWriteLock.Wr
- 6.32 非公平信号量historical
1. 是什么 限流,使用的非公平策略 2. 使用 3. 原理分析 3.1. 构造方法 3.1.1. 非公平Sync - NonfairSync - Sync 3.2. acquire 3.2.1. 调用AQS加共享锁 - AQS acquireSharedInterruptibly 3.2.1.1.
- 6.33 Thread.sleephistorical
1. sleep结束后什么时候唤醒 假设某个线程在 2020-01-09 22:47:54 运行上面这段代码,那么他会在 2020-01-09 22:47:55 立马往下执行么? - 答案 不一定,这段代码的意思只是在 2020-01-09 22:47:55 有机会去抢占CPU,而不是获得了CPU并
- 6.34 非公平锁historical
所谓公平锁,就是只要锁已经被释放了,那么不管是先到的还是后到的,都可以去抢占锁 1. 如何使用 2. 实现原理 2.1. 构造方法 2.2. 加锁 2.2.1. 使用非公平锁加锁 - NonfairSync lock方法 2.2.2. 通过AQS加锁 - AQS.acquire方法 由于Nonfai
- 6.35 LinkedBlockingQueuehistorical
1. 是什么 使用单向链表实现的有界的阻塞队列 读读、写写相互阻塞,读写不相互阻塞 吞吐量比ArrayBlockingQueue高 2. 如何使用 3. 源码分析 3.1. 构造方法 3.1.1. 底层使用单向链表+Lock+Condition实现 3.1.2. Node 结构如下图: 3.2. p
- 6.36 RejectedExecutionHandlerhistorical
1. RejectedExecutionHandler是什么 corePoolSize满了,blockingQueue也满了,maxPoolSize也满了,那么新的任务该怎么处理,这就看RejectedExecutionHandler 2. 分类 2.1. CallerRunsPolicy 处理策略
- 6.38 公平ReadWriteLockhistorical
1. 是什么 无论队列前面是否有人排队等待锁,我直接去抢 2. 怎么使用 3. 源码分析 3.1. uml 3.2. 构造方法 - ReentrantReadWriteLock - ReentrantReadWriteLock.ReadLock - ReentrantReadWriteLock.Wr
- 6.39 公平信号量historical
1. 是什么 限流,使用的公平策略 2. 使用 3. 原理分析 3.1. 构造方法 3.1.1. 公平Sync - FairSync - Sync 3.2. acquire 3.2.1. 调用AQS加共享锁 - AQS acquireSharedInterruptibly 3.2.1.1. 尝试加锁
- 6.40 线程状态historical
1. 线程状态枚举类 2. 状态流转 3. 参考 - Lifecycle and States of a Thread in Java \- GeeksforGeeks
- 6.41 PriorityBlockingQueuehistorical
1. 是什么 底层使用数组(二叉堆)实现的无界的阻塞队列 读读、读写、写写相互阻塞 可以排序 由于无界,所以put操作不会阻塞,但是take操作会阻塞(队列为空的时候) 1.1. 二叉堆 一颗完全二叉树,堆序性质为,每个节点的值都小于其左右子节点的值,二叉堆中最小的值就是根节点。 底层用数组进行存储
- 6.42 SynchronousQueuehistorical
1. 是什么 底层使用单向实现的阻塞队列,不存储元素 一个写者必须同时有一个读者才能进行下去,反之亦然。 否则写者将会一直阻塞或者读者将会一直阻塞 2. 使用 3. 原理 3.1. 构造方法 3.1.1. Transfer 3.1.2. QNode 3.2. put 阻塞 3.2.1. 调用Tran
- 6.43 Atomichistorical
1. 是什么 线程安全的原子类,底层使用CAS实现 2. 使用 以AtomicInteger为例 3. 原理分析 3.1. 构造方法 可以看到主要有三个属性: Unsafe unsafe 、 long valueOffset 和 volatile int value - 关于Unsafe类的解释参考