“锁”,程序员不可逾越的鸿沟

786 阅读7分钟

两个男人相对而坐,离的很近,近到可以听到彼此的呼吸生 酷酷男率先发问:了解锁多么 知道点, 酷酷男:解释下什么是锁?

一种保护线程安全的机制,保证多线程操作数据的正确性和一致性,

酷酷男:知道锁的分类么?

悲观锁,乐观锁,独占锁,共享锁,公平锁,非公平锁,分布式锁,自旋锁

酷酷男:你怎么理解乐观锁悲观锁的

大家都放在数据库来讲(其实这两个概念是属于计算机的,不要被误导),以mysql为例,悲观锁,主要是表锁,行锁还有,间隙锁,叶锁,读锁,因为这些锁在使用时势必引起线程阻塞,我们称之为悲观 另外乐观锁在mysql中其实不存在,只是mvcc的机制,类似乐观锁, 你说的mvcc,可以解释下么

在mysqll的 innodb引擎下,mvcc是实现事务隔离主要方式,通过版本号的方式, 避免数据在不同事务出现错乱,mvcc只在RR和RC事务级别为,才会生效,其次可以通过mvcc避免写数据时对于读的阻塞 眼睛男:具体mvcc机制有什么? 在每开启一个事务时,会生成一个事务的版本号,被操作的数据会生成一条新的数据行(临时),但是在提交前对其他事务是不可见的,对于数据的更新操作成功,会将这个版本号更新到数据的行中,事务提交成功,将新的版本号,更新到此数据行(永久)中,这样保证了每个事务操作的数据,都是相互不影响的,也不存在锁的问题; 眼睛男:如果多个事务同时提交,怎么判断谁成功 mysql判断,其实就是谁先提交成功算谁的 酷酷男:那再说下事务吧,

事务常说一系列操作要么都成功要么都失败,主要特性acid,事务的的实现主要依赖两个log:redo-log,undo-log,事务会记录数据修改前的数据undo-log,修改后的数据放入redo-log,提交成功则用redo-log 更新到磁盘,失败则使用undo-log将数据恢复到修改前的数据

酷酷男:你认为独占锁,共享锁怎么回事 (嗯,独占,共享,公平,非公平,自旋锁这些都是广泛的概念,很多语言都有,包括操作系统,js的同学请回避) 独占锁持锁的线程只能有一个,共享锁可以被多个线程同时持有

酷酷男:独占可以理解,你认为共享的意义是什么

共享锁是为了提高程序的并发读,比如数据的操作有读有写,通常写的操作加锁,保证数据正确性,而对于读操作如果无锁,恰好此时写读操作也在进行,读的数据有可能不是最新数据,如果对读操作加独占锁,那么读读之间也发生互斥这是不合理,就衍生共享锁,读的的操作就用共享的锁,只对写互斥,即保证了并发度有保证了正确性,在java中的读写锁就是提供这种机制的

酷酷男:读写锁怎么工作的?

在java中读写锁(ReadWritelock),离不开AbstractQueuedSynchronizer,简称AQS这是java实现的一种锁机制,互斥锁,读者写锁,条件产量,信号量,栅栏的都是j基于AQS实现的,工作机制依赖CHL队列,volatile 关键字修饰的状态符stat,线程修改stat成功表示线程持锁成功,失败了就进CHL队列等待唤醒,等待唤醒,AQS中还有一个重要的机制自旋,在线程等待唤醒过程中,一旦锁被释放会使用自旋(while(!cas()))的方式,不停的尝试获取锁,直到其他线程获取成功或者自己获取成功 在ASQ中共享与独占不同的是,CHL队列中的节点的模式是EXCLUSIVE还是SHARED,当一个线程成功修改了stat状态,表示获取了锁,如果线程所在的节点为SHARED,将开始一个读锁传递的过程,从头结点,向队列后续节点传递唤醒,直到队列结束或者遇到了EXCLUSIVE的节点,等待所有激活的读操作完成,然后进入到独享模式 公平与非公平的区别就是一行代码,在线程获取锁是,,是直接进队列还是直接修改stat,这是基本的工作机制,详细的估计可以再聊好久

酷酷男:java 还有那些锁的实现方式

在java中,synchronized关键字,称为内置锁,被synchronized修饰的方法或者代码块,只允许一个线程执行,是典型的独享锁,synchronized的机制,可以参考AQS的实现方式,只是AQS使用显示的用lock.lock()调用,对于synchronized,可以认为在修饰的地方,添加了lock方法,结束的地方进行了unlock释放锁的方法,只是我们看不到。 它本身实现有两部分:monitor对象,线程,工作机制还是线程抢占对象使用权,对象都有自己的对象头,存储了对象的很多信息,其中有一个是标识被哪个线程持有,对比AQS,线程从修改stat,变为修改monitor的对象头,线程的等待区域动 AQS中的队列,变为monitor对象中的某个区域,

酷酷男:刚才我们一直围绕线程在聊,那么锁在内存中的操作过程是什么

这里必须要说内存模型(这个和jvm不要混淆,The Java memory model used internally in the JVM divides memory between thread stacks and the heap. This diagram illustrates the Java memory model from a logic perspective),是JVM用来区别线程栈和堆的内存方式,每个线程在运行的时候,所操作的数据存储空间有两个,主内存 工作内存,主内存其实就是jvm中堆,工作内存就是线程栈锁操作的CPU缓存区(被所有线程共享),每次操作,从主内存中把数据读到工作内存中,然后在工作内存中进行各种处理,如果数据被修改,会把数据回写到主内存,然后其他线程又进行同样的操作,就这样数据在工作内存和主内存,进进出出,不亦乐乎,在多线程的情况,就是因为进进出出的顺序乱了,不是按照线程预期的访问顺序,就出现了数据不一致的问题,导致了多线程的不安全性;

酷酷男:内存模型还要那些可以聊的?

happen-befor 原则,Volatile 关键字(,内存屏障

酷酷男:哦(怂了怂了)

happen-befor原则定义了内存模型执行过程中的定律,就像1+1 = 2,不可能被打破的jvm的运行机制都依赖于这个原则,是jvm的法律!!! Volatile关键字就有点厉害了,Volatile修饰的数据,在被某个线程修改后,会被的回写到主内存(写的事时间不一定),然后其他线程再获取时,就是新的数据,听起来很美好,但是Volatile没有办法控制线程的顺序,当一个数据(新数据)即将被修改到主内存时,刚好,另外一个线程从主内存读了数据(老数据),并又进行了一波操作,又将数据(更新的数据)回写到了主内存,整个过程(新数据)完全没有起到一毛钱作用,最终导致了数据的错误,呼呼打完收工!!!!

你懂的很多么 那是因为我帅, 有多帅? 可以用微笑杀死你 来啊!

猝 享年28岁!!!

。。。。。end。。。。。。

下面我们看下“锁”范围内的知识覆盖体系有那些

更详细的请打开http://treenpool.com/html/index.html?branchId=488

欢迎大家加微信骚扰:treenpool 关注微信公众号:treenpool,更多好文在里面 请持续关注,treenpool.com