对线程中锁的理解

  • Post author:
  • Post category:其他




对线程中锁的理解



在并发编程时,我们经常会遇到多个线程访问同一个共享变量,当同时对该共享变量做读写操作时,就会产生数据不一致的问题。基于这个场景,我们要讨论的内容–



产生了。

首先引发一个问题。

锁是什么


锁,是一种线程中的一种同步机制。通过加锁,可以实现对共享资源的互斥访问。


锁有哪些


根据不同维度,有不同类型的锁。但深究下来,其实也就是由如下两种情况产生。


.

使用synchronized关键字修饰成员变量、代码块、方法产生的锁。


.

并发工具包java.util.concurrent.lock包下的一些实现了Lock接口的类,常见的诸如可重入锁RetrantLock,可重复读写锁RetrantReadWriteLock。


可重入锁


**.**指在同一个线程中,在外层方法获取锁的时候,进入内层方法会自动获取锁。为避免死锁的产生,JDK中基本都是可重入锁。


公平锁 / 非公平锁


**.**公平锁,指多个线程按照申请锁的顺序获取锁。

如java.util.concurrent.lock.RetrantLock.FairSync

**.**非公平锁,指多个线程获取锁的顺序并不按照申请锁的顺序,有可能后申请的线程先获得锁。如synchronized,java.util.concurrent.lock.RetrantLock.NonfairSync。


悲观锁 / 乐观锁



.

悲观锁,总是考虑最坏的情况,每次操作数据的时候,认为数据一定会被其他线程修改。


.

适用场景:写操作比较多,先加锁可以保证写操作时数据的正确性。


.

悲观锁的实现有如下方式


.

Java代码层面,使用synchronized关键字,接口Lock的实现类,如RetrantLock。


.

MySQL中的行锁、表锁、读锁、写锁或诸如select…for update


.

乐观锁,表现的很乐观,认为数据不会被其他线程修改。所以不会一开始就加锁,只有在更新的时候会判断该数据是否被修改。

**.**适用场景:读多写少。可提高吞吐量

乐观锁有如下方式实现


.

java代码中的automic包下的类如AutomicInteger或通过CAS算法实现。


.

数据库层面给数据库表添加一个数字类型的“version”字段或者添加一个timestamp时间戳类型的字段。


独占锁 / 共享锁


**.**独占锁,指锁一次只能被一个线程所持有。

synchronized、java.util.concurrent.lock.RetrantLock都是独占锁。


.

共享锁,指锁可被多个线程所持有。

ReadWriteLock返回的ReadLock就是共享锁


自旋锁



.

指尝试获取锁的线程不会立即阻塞,二十采用循环的方式去尝试获取锁。


.

作用:自旋锁可减少线程上下文切换的消耗。


.

缺点:循环占有,浪费CPU资源。


偏向锁 / 轻量级锁 / 重量级锁


严格上来所,这几个锁是对关键字synchronized的加锁的状态升级的描述。

通过synchronized加锁后,锁会有如下变化。

**.**偏向锁,一段同步代码一直被同一个线程访问,那么该线程获取的就是偏向锁。

**.**轻量级锁, 偏向锁被一个其他线程访问,java对象的偏向锁就会升级为轻量级锁。


.

重量级锁,其他线程以自旋的方式尝试获取锁,不会发生阻塞,自旋到一定次数仍未获取到锁,轻量级锁就会升级为重量级锁。



版权声明:本文为Qcsdn123原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。