JAVA 锁

发布时间:2024年01月23日


CAS??
全称是 compare and swap (比较再交换),它是一种乐观锁的体现,可以在无锁的情况下保证线程共享数据的原子性。
为什么要让CAS
所有线程都是在运行着,没有发生阻塞,
ABA问题
在这个线程中CAS操作拿到共享数据和自己的预期值进行比较的时候,另外一个线程很快拿到共享变量改完,又改了回去。这个CAS操作还是会成功,虽然结果上是一样的,但是有些时候还是会对整个程序又影响。
我淘宝购物又退货。虽然从结果来看,商家和我都没有改变什么,但是本质上这件事是发生过的,这种现象是存在的。
悲观锁
对于这个线程的共享变量,我上锁了,你们其他线程都不能修改,我修改完了,把锁释放了,你们才能修改
synchronized
重量级锁
synchronized(对象锁)采用互斥的方法,让同一时刻只能有一个线程持有对象锁
它的底层由monitor实现的,monitor是jvm的对象,线程获得对象锁需要关联monitor
在monitor中,涉及到了用户态和内核态的转换,进程的上下文切换。开销较大,性能较低。
monitor有三个属性,分别是owner,entrylist,waitset
owner是关联的锁的线程,并且只能关联一个线程。entrylist关联的是出于阻塞状态的线程,waitset关联的是处于等待状态的线程
偏向锁和轻量级锁
轻量级锁
线程加锁的时间是错开的(也就是没有竞争),可以使用轻量级锁来优化。轻量级锁修改了对象头的锁标志,相对于重量级做性能提升很多,每次修改都是CAS操作,保证了原子性。
偏向锁? ? ? ? ??
如果在一段很长的时间里都是同一个线程使用锁,这时可以使用偏向锁来优化。在第一次获得锁的时候,会进行一次CAS操作,之后这个线程再次获取锁的时候,只需要在锁的对象头上判断是否是自己的线程ID即可,比每次都有进行CAS操作的开销要低。
锁升级
Reentrantlock
表示支持可重新进入的锁,调用lock()方法获取到锁之后,再次调用lock是不会堵塞的
底层主要使用CAS+AQS队列来实现
支持公平锁和非公平锁,默认是非公平锁,可以在构建对象的时候传入一个参数来修改为公平锁。

synchronized? 和 Lock的区别
语法层面来讲:
synchronized是一个关键字,锁住的是对象,lock是一个接口,锁住的是线程。
synchronized会自动释放锁,Lock需要手动释放锁
功能层面:
都是悲观锁,都具备基本的互斥,同步,锁重入等功能
Lock提供了更多的功能,比如公平锁
Lock有更多的实现场景,比如reentrantLock,reentrantkReadWriteLock(读写锁)
性能层面
在没有竞争的情况下,synchronized也做了很多优化,性能还不错
在竞争激烈的情况下,lock的性能会更好一些
乐观锁
先执行一下再去对比,如果和之前的值不一样,就会再重试。
应用场景:在操作数据库的时候,在数据表中添加
版本号
在数据表中添加版本号,每次修改数据就将版本号+1。在更新数据的时候,先读取到数据的版本号,然后进行比对,如果相同,就进行修改成功。如果不相同,就说明有其他事务已经修改过这条数据,就需要进行回滚,然后重试。
时间戳
和版本号同理,时间戳记录最后更新时间。
AQS
是多线程的队列同步器,是一种锁机制
他维护了一个先进先出的双向队列,用来保存排队的线程
它里面有一个state参数,0表示无锁,1表示加锁。线程通过修改这个参数来加锁获取到了资源。
多个线程之间通过CAS操作来保证这个参数的原子性。
公平锁和非公平锁
排队点单的例子
公平锁和非公平锁
可重入锁和不可重入锁
可重入锁
一个线程可以多次的进行加锁,解锁也需要相同次数的解锁
ReentrantLock
Synchronized
不可重入锁
双重检测锁
是单例设计模式中的双重检测锁,避免对象的多次实例化
为什么会有双重检测锁
我来获取对象的时候,不管这个对象创没创建,我都要先加锁才能完成这个操作,导致很慢。所有就有了双重检测锁。
怎么做的
出现并发问题的根本原因
原子性
就是指一个线程在CPU中的执行是不可中断的也不可暂停的,要么执行完成,要么不执行。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
如何保证原子性: 加锁 Synchronized? 和 lock
可见性
就是在JMM内存模型中,线程之间是不可见的,这就导致多个线程对于共享数据同一时刻进行了同样的操作。? ? ? ? ? ? ? ? ? ? ??
如何保证可见性:? 1.在共享变量数据上添加volatile关键字? 2.加锁Synchronized??和?lock
有序性
CPU为了提高运行效率,它会对代码的执行顺序进行优化,CPU中执行的顺序不是你代码中的顺序,但是执行结果是一致的。
如何保证有序性: 使用volatile关键字,禁止进行指令重排序
死锁
两个或多个线程互相需要对方的资源,但无法获取资源,导致其他线程一直无法执行
如何避免死锁
减少锁的使用或者细化锁的粒度,使用定时锁,保证锁的顺序。
线程安全
什么是AIO?BIO?NIO
AIO
AIO是一个有效请求一个线程?非堵塞同步通信模式,采用异步通道实现通信,其基于事件和回调机制
BIO
BIO是一个连接一个线程,堵塞同步通信模式,客户端与服务器连接需要三次握手,使用简单但吞吐量少
NIO
NIO是一个请求一个线程,非堵塞同步通信模式,客户端与服务器需channel连接,始终只有一个线程,
并发编程的常见陷阱和解决方案
例如死锁,饥饿,活锁等问题,以及如何使用并发编程最佳避免这些问题
死锁,两个或多个线程互相需要对方的资源,但无法获取资源,导致其他线程一直无法执行,解决办法,减少锁的使用或者细化锁的粒度,使用定时锁,保证锁的顺序。
活锁:两个或多个线程互相干扰对方的进程,解决办法,改善算法
饥饿:某个线程无法获取cpu的时间片,无法进行资源共享,一直进人等待阶段,使用公平说,线程优先等。

文章来源:https://blog.csdn.net/weixin_55029324/article/details/135711862
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。