并发编程
文章平均质量分 63
码上一元
8年Java后端开发,5年技术组长,先后负责过财务系统、大数据中台、基础架构平台,现担任某互联网公司财务项目负责人。
展开
-
线程池
创建对象,仅仅是在JVM的堆内存里分配一块内存;创建一个线程,需要调用操作系统内核的API,然后操作系统要为线程分配一系列资源。所以线程是一个重量级对象,应避免频繁的创建和销毁。...原创 2021-01-27 13:17:19 · 183 阅读 · 0 评论 -
乐观锁
基于Synchronized和Lock实现的同步锁机制,属于悲观锁,保护线程安全最直观的方式。悲观锁在高并发场景下,激烈的锁竞争会造成线程阻塞,大量阻塞线程会导致系统的上下文切换,增加系统的性能开销。乐观锁:在操作共享资源时,总是抱着乐观的态度执行,认为自己可以成功的完成操作;但当多个线程同时操作一个共享资源时,只有一个线程会成功,而失败的线程不会像悲观锁一样在操作系统中挂起,而仅仅是返回,并且系统允许失败的线程重试,也允许自动放弃退出操作。所以,乐观锁相对悲观锁来说,不会带来死锁、饥饿等活.原创 2021-01-25 13:33:33 · 291 阅读 · 1 评论 -
并发容器123
Java在1.5版本之前所谓线程安全的容器,主要是指同步容器,最大的问题是性能差,所有的方法都用synchronized来保证互斥,串行度太高;Java在1.5及之后的版本提供了性能更高的容器,称为并发容器。ListCopyOnWriteArrayList 写时复制,写的时候会将共享变量新复制一份出来,好处是读操作完全无锁;其内部维护了一个数组,成员变量array指向这个内部数组,所有的读操作都是基于array进行的。在遍历array的同时,还有一个增加元素的写操作,Cop..原创 2021-01-18 14:22:35 · 99 阅读 · 0 评论 -
比读写锁更快的StampedLock
StampedLock支持的三种模式ReadWriteLock支持两种模式:读锁、写锁;StampedLock支持三种模式:悲观读锁、写锁、乐观读;StampedLock的悲观读锁和写锁 与 ReadWriteLock的读锁和写锁 语义相似,允许多个线程获取悲观读锁,只允许一个线程获取写锁,写锁和悲观读锁是互斥的。不同的是StampedLock的悲观读锁和写锁在加锁成功后,都会返回一个stamp,解锁时,需要传入这个stamp。final StampedLock sl = new St原创 2021-01-13 16:50:44 · 196 阅读 · 1 评论 -
ReadWriteLock:分场景优化性能,提升易用性
普遍的并发场景:读多写少,针对这种场景,Java SDK并发包提供了读写锁ReadWriteLock读写锁遵守的三条基本原则:允许多个线程同时读共享变量; 只允许一个线程写共享变量; 如果一个线程正在写共享变量,此时禁止读线程读共享变量;读写锁与互斥锁的一个重要区别是:读写锁允许多个线程同时读共享变量,而互斥锁是不允许的,这也是读写锁在读多写少场景下性能优于互斥锁的关键。读写锁内部维护了两个锁:读操作的ReadLock,写操作的WriteLock。读写锁如何实现锁分离保证共享资源..原创 2021-01-12 17:09:42 · 214 阅读 · 0 评论 -
Semaphore信号量模型
信号量模型简单概括为:一个计数器、一个等待队列、三个方法init():设置计数器的初始值;down()/P操作:计数器的值减1;如果此时计数器的值小于0,则当前线程将被阻塞,否则继续执行;up()/V操作:计数器的值加1;如果此时计数器的值小于或等于0,则唤醒等待队列中的一个线程,并将其从等待队列中移除;上述三个方法都由信号量模型的实现方来保证原子性。代码化的信号量模型class Semaphore{ // 计数器 int count; // 等待队列 Q原创 2021-01-12 13:59:58 · 168 阅读 · 0 评论 -
Lock和Condition简结
并发编程两大核心问题互斥:同一时刻只允许一个线程访问共享资源;同步:线程间如何通信、协作;Java SDK并发包通过Lock和Condition两个接口实现管程,Lock解决互斥问题,Condition解决同步问题。Java提供的synchronized也是管程的一种实现,既然Java语言层面已经实现了管程了,为什么还要在SDK里提供另外一种实现呢?死锁发生的4个条件互斥,共享资源X和Y只能被一个线程占用; 占有且等待,线程T1已经取得共享资源X,在等待共享资源Y的时候,不释放.原创 2021-01-11 16:42:32 · 339 阅读 · 4 评论 -
线程的生命周期简结
通用的线程生命周期初始状态:线程已被创建,还不允许分配CPU执行。(在操作系统层面,线程还未被真正创建) 可运行状态:线程可以分配CPU执行,线程被真正创建。 运行状态:当有空闲的CPU时,操作系统会将其分配给一个处于可运行状态的线程,被分配到CPU的线程状态就转换成了运行状态。 休眠状态:运行状态的线程如果调用了一个阻塞的API或者等待某个事件,线程的状态就会转换到休眠状态,同时释放CPU使用权;当等待的事件出现了,线程就会从休眠状态转换到可运行状态。 终止状态:线程执行完或者出现异常就会原创 2021-01-07 14:09:29 · 112 阅读 · 0 评论 -
深入理解“管程模型”
管程:指的是管理共享变量以及对共享变量的操作过程,让他们支持并发。(管理类的成员变量和成员方法,让这个类是线程安全的)MESA模型互斥:同一时刻只允许一个线程访问共享资源;同步:线程之间如何通信、协作。管程解决互斥问题:将共享变量及其对共享变量的操作统一封装起来;管程解决同步问题:public class BlockedQueue<T>{ final Lock lock = new ReentrantLock(); // 条件变量:队列不满 f..原创 2021-01-06 15:33:53 · 1059 阅读 · 0 评论 -
多线程—深入理解“锁优化”
在并发编程中,多个线程访问同一个共享资源时,需要考虑如何维护数据的原子性JDK1.5之前,Java依靠Synchronized关键字来实现原子性,Synchronized是JVM实现的一种内置锁,锁的获取、释放由JVM隐式实现。JDK1.5版本,Java依靠并发包中的Lock接口实现锁功能,使用时需要显示获取和释放锁。Synchronized是基于底层操作系统的Mutex Lock实现的,每次获取和释放锁都会带来用户态和内核态的切换,从而增加系统性能开销,因此在锁竞争激烈的情况下,Synch.原创 2021-01-05 16:55:05 · 121 阅读 · 0 评论 -
等待通知机制优化循环等待
等待-通知机制:线程首先获取互斥锁,当线程要求的条件不满足时,释放互斥锁,进入等待状态;当要求的条件满足时,通知等待的线程,重新获取互斥锁。synchronized 实现等待-通知机制class Allocator { private List<Object> als; // 一次性申请所有资源 synchronized void apply( Object from, Object to){ // 经典写法 while(als.contains.原创 2021-01-04 17:35:39 · 125 阅读 · 0 评论 -
死锁怎么解决?
只有拿到转出账户和转入账户的资源,才能转账成功。首先尝试锁定转出账户this,然后尝试锁定转入账户target,只有两者都成功时,才可以执行转账操作。class Account { private int balance; // 转账 void transfer(Account target, int amt){ // 锁定转出账户 synchronized(this) { ① // 锁定转入账户 synchronized(.原创 2020-12-31 14:21:58 · 179 阅读 · 4 评论 -
互斥锁:解决原子性问题
原子性:一个或多个操作在CPU执行的过程中不被中断的特性原子性问题的源头是线程切换,操作系统做线程切换依赖于CPU中断,所以禁止CPU中断就能禁止线程切换。示例:在32位CPU上执行long类型变量的写操作,long类型变量是64位,所以会被拆分为两次写操作(写高32位和写低32位)1.单核CPU场景下,同一时刻只有一个线程在执行,禁止CPU中断,意味着操作系统不会重新调度线程,也就是禁止了线程切换,获得CPU使用权的线程就可以不间断执行,所以两次写操作,要么都被执行,要么都没有被执行,具备.原创 2020-12-29 14:47:04 · 133 阅读 · 0 评论 -
Java内存模型:解决可见性、有序性
Java内存模型规范了JVM如何提供按需禁用缓存和编译优化的方法,包括volatile、synchronized和final关键字,以及6项Happens-Before原则。volatile:禁用CPU缓存。如声明一个变量 volatile int x = 0;它表达的是,告诉编译器,对这个变量的读写,不能使用CPU缓存,必须从内存中读取或写入。Happens-Before原则:前面一个操作的结果对后续操作是可见的。程序的顺序性规则:在一个线程中,按照程序顺序,前面的操作Happe..原创 2020-12-28 16:14:11 · 85 阅读 · 0 评论 -
并发问题的根源:CPU/内存/IO设备的速度差异
CPU、内存、IO设备的速度差异程序整体的性能取决于最慢的操作 — 读写IO设备为了合理利用CPU的高性能,平衡三者的速度差异,计算机体系结构、操作系统、编译程序做了以下优化:CPU增加了缓存,以均衡与内存的速度差异; 操作系统增加了进程、线程,以分时复用CPU,进而均衡CPU与I/O设备的速度差异; 编译程序优化指令执行顺序,使得缓存能够更加合理的利用。并发程序的问题根源1.缓存导致的可见性问题单核时代:所有的线程都是在一颗CPU上执行,一个线程对缓存的写,另一个线程一定是.原创 2020-12-28 11:00:17 · 1901 阅读 · 0 评论 -
跳出来看全景,钻进去看本质,并发编程的3个核心问题
并发编程3个核心问题:分工:如何高效的拆解任务并分配给线程。如 Fork/Join框架、Executor 同步:线程间如何协作。如 CountDownLatch、CyclicBarrier、Phaser、Exchanger 互斥:保证同一时刻只允许一个线程访问共享资源。如 可重入锁管程:是一种解决并发问题的通用模型在并发编程领域,解决协作问题的核心技术是管程为了提高计算机执行任务的效率,所以IO等待的时候不能让cpu闲着,所以我们把任务拆分交替执行,有了分时操作系统,出现了并发。后来.原创 2020-12-22 17:03:18 · 159 阅读 · 0 评论 -
自定义异常的性能问题:避免打印繁重的“栈”信息
Java每实例化一个Exception,就会对当时的栈进行快照,如果该动作发生的比较频繁,对内存的开销也就不可忽略了!常见的自定义异常格式:public class ParamException extends RuntimeException{ public ParamException() { super(); } public ParamException(String message) { super(message); }原创 2020-12-21 15:11:51 · 590 阅读 · 0 评论 -
CompletionService 实现 Dubbo 中的 Forking Cluster
CompletionService 实现 Dubbo 中的 Forking Cluster并行的查询三个业务数量接口,拿到最先返回的数据,并取消剩下的任务public class CompletionServiceDemo { public static void main(String[] args) { Integer num = getNum(); System.out.println("最终的数据量:" + num); } .原创 2020-12-11 18:33:31 · 136 阅读 · 2 评论