Java并发编程

并行与并发

并行:在同一时刻可进行多种操作

并发:在同一时间段发生多种操作,微观上是串行操作

多线程

解决性能问题

提升CPU的利用率

多线程并行问题

安全性 性能(CPU的切换)

死锁

可见性 有序性 原子性

java内存模型(JMM)

硬件中: CPU --内存-- IO 设备上存在速度的差异问题

做出了以下优化

CPU 添加了缓存,以环节与内存直接的速度差异

操作系统添加了线程进程,分时复用CPU,均衡CPU与IO的速度差异

编译代码优化,对代码进行重排

java内存模型

java内存模型就是规范计算机内存与jvm之间的协同工作

屏蔽了硬件和操作系统之间的内存差异问题,让java程序在各种平台下保持一致并发的状态,比如在修改一个共享变量何时以及怎样被看到之类的…

JMM规定了所有的变量存储在主内存当中,每个线程拥有自己的工作内存,线程对内存中的变量必须要在自己工作区域进行,不能直接读写主内存。

缓存

在多处理器的系统中,每个处理器都有自己的高速缓存区,并且共享同一个主内存。当同时对数据进行处理时,导致各自的缓存区数据不一致,需要维护这个数据的一致性。

并发编程的核心问题

可见性

线程内部缓存造成的问题.

一个线程对共享变量的操作不能被其他线程及时看见.

多核处理器,每个CPU有自己的缓存,并仅自己的处理器可见.CPU缓存与内存数据不一定保持一致.

原子性

线程切换带来的原子性问题.

原子性是拒绝多线程交叉操作的,但由于线程的切换,其他线程也会执行,造成结果与预期不一致

有序性

编译优化带来有序性问题

CPU为了优化性能在编译期间可能会对指令进行优化重新排序,会影响多线程并发执行的正确性.

如何保证原子性

同一时刻只有一个线程进行修改

syschronized

juc–原子变量

CSA(比较并交换)

乐观锁+自旋锁,轻量级锁机制

三个操作数

主内存值 V

预期值(比较时,从主内存中再次获得的值) A

更新后的值 B

如果V==B 说明没有其他线程对共享操作主内存数据,将主内存的值更新为操作后的值

如果V!=B 说明有其他线程对此进行操作,则重新读取主内存中的值,在进行上述操作,直至相等 (自旋)

缺点:

因为自旋过程,导致CPU消耗.并发量大,CPU跑满…

ABA问题 某线程将AB 在将BA 其他线程误判以为没有被修改过而产生问题.

解决:为内存值添加版本号,这样就可以通过版本号进行比较是否经过修改

concurrentHashMap

支持高并发并且线程安全的容器.

1.8后,内部放弃分段锁,采用CAS和synchronized的方式,提高效率

分段锁:容器里面有多把锁,每一个锁用于锁住容器中的一部分,那么多线程访问容器不同数据段的数据时,不会出现锁竞争的情况.

放弃分段锁的原因:

1.加入多个分段锁浪费内存空间

2.生产环境下,放入同一段的可能性比较低,造成更新操作等待时间长

3.为了提高GC效率

放弃分段锁,采用node锁,利用CAS和synchronized,降低锁的粒度,提高性能.

在进行插入操作时,首先判断被插入数据是不是链表的第一个结点,如果是,利用CSA原则,进行插入,如果不是,对链表的第一个结点进行synchronized加锁.

java中锁的分类

并不是指锁的状态,有的指设计.

乐观锁/悲观锁

乐观锁:是认为对于同一个数据的并发操作,是不会发生修改的.

不加锁编程,用的是CAS算法.必须原子类

适合读操作多的场景

悲观锁:是认为对于同一个数据的并发操作,一定会发生修改的

适合写操作多的场景,加锁,synchronized

公平锁/非公平锁

公平锁:等待锁的线程是排队等待锁的释放,等待时间最久的线程获得锁

​ 需要维护一个等待队列,相比于非公平锁,效率低

非公平锁:锁释放后,直接获取,哪个线程获取到执行

可重入锁

在同一个线程内,获得了外层方法的锁时,再进入内层带锁的方法可以直接获得锁。可以避免死锁

读写锁(ReadWriteLock)

特点:

多个读者可以同时进行

写者必须是互斥的(读者和写者也不能同时进行)

写者优先于读者(有写者,读者等待,优先唤醒写者)

分段锁

就是将数据进行分段,然后给每一段加锁,将锁的粒度细化,提高效率

jdk1.8之前的concurrentHashMap就是采用这种锁

自旋锁

在没有获得锁直接,不断的进行重试或者锁,避免进入阻塞状态,但是比较消耗CPU

共享锁与独占锁

共享锁:多个线程可以共享一个锁 读写锁中的读

独占锁:一次只能有一个线程持有该锁

AQS(抽象的队列同步器)

若当前共享资源被占用,那么久需要一个线程阻塞等待和唤醒时锁的分配机制,机制就用到CLH同步队列锁。通俗的说没有获得锁的其他线程,加入到一个等待队列当中,进入阻塞状态,当锁释放后,等待队列的第一个线程获得锁

基于CLH队列,用volatile修饰一个共享变量状态,通过CAS改变这个状态符,成功则获得锁,失败就进入队列等待。

锁的几种状态

锁的状态是通过对象监视器在对象头的来字段表示的,jvm为了提高获得锁和释放锁的效率进行的优化

无锁

偏向锁

当一直只有一个线程获得锁对象,此时就会将对象头中的状态改为偏向锁,并且会记录该线程的id

轻量级锁

当前锁时偏向锁时,有了另一个线程访问,偏向锁就会升级为轻量级锁,线程以自旋的形式来尝试获取锁。

重量级锁

当前锁时轻量级锁时,另一个线程处于自旋状态获取锁,但是到一定的次数还未获得锁的时候就会进入阻塞状态,锁就升级为重量级锁。重量轻锁让其他获取锁的线程进入阻塞状态,性能低。

synchronized的实现

是由jvm实现的一种互斥同步方式,被synchronized修饰的代码块,在编译时会有两个指令(进入,退出),当一个线程执行进入指令时,会尝试获取锁,如果当前线程获取了这个锁,那么锁的计数器就会+1,执行退出指令,锁计数器-1。

java对象头:用于存储对象自身的一些数据,还有锁的状态也是在此进行提现的

ReentrantLock

API层面的互斥锁,获取锁使用CAS操作,如果获得锁,就将锁的状态修改为1,获取不成功就加入到等待队列当中。

支持公平锁和非公平锁。

手动的声明锁和释放锁。

创建锁对象时,如果传入参数true就代表为公平锁,如果默认无参就是不公平锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值