【java学习】java锁

来源:互联网 发布:linux主目录是什么 编辑:程序博客网 时间:2024/06/06 03:13

1,概念

1)Java的线程是映射

Java的线程是映射到操作系统的原生线程之上的,如果要阻塞或唤醒一个线程,都需要操作系统来帮忙完成,这就需要从用户态转换到核心态中,因此状态装换需要耗费很多的处理器时间,对于代码简单的同步块(如被synchronized修饰的getter()和setter()方法),状态转换消耗的时间有可能比用户代码执行的时间还要长。

2,自旋锁

1)概念

自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时才能进入临界区。

2)场景

由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。
当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。如果线程竞争不激烈,并且保持锁的时间段少,适合使用自旋锁。

自旋等待避免了线程切换的开销,但要占用处理器时间的,因此,如果锁被占用的时间很短,使用自旋锁。因此,自旋等待的时间必须要有一定的限度,如果自旋超过了限定次数(默认是10次,可以使用-XX:PreBlockSpin来更改)没有成功获得锁,就应当使用传统的方式去挂起线程了。

自旋是在轻量级锁中使用的,在重量级锁中,线程不使用自旋。

3)实现

public class SpinLock {  private AtomicReference<Thread> sign =new AtomicReference<>();  public void lock(){    Thread current = Thread.currentThread();    while(!sign .compareAndSet(null, current)){ //使用CAS原子操作,设置sign为当前线程,并且预测原来的值为空      }  }  public void unlock (){    Thread current = Thread.currentThread();    sign .compareAndSet(current, null);//使用CAS原子操作,设置sign为null,并且预测值为当前线程。  }}

当有第二个线程调用lock操作时,由于sign值不为空,导致循环一直被执行,直至第一个线程调用unlock函数将sign设置为null,第二个线程才能进入临界区。

4)常见自旋锁

①TicketLock

Ticket锁主要解决的是访问顺序的问题,主要的问题是在多核cpu上。

②CLHlock

点击查看详情

③MCSlock

点击查看详情

3,乐观锁和悲观锁

1)概念

锁有两种:乐观锁与悲观锁。

①乐观锁

假定不会发生并发冲突,只在提交操作时检测是否违反数据完整性。(使用版本号或者时间戳来配合实现)。
在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。。

乐观锁用到的机制就是CAS。

②悲观锁

有强烈的独占和排他特性。
不相信数据是安全的,故在数据处理过程中必须全部上锁,屏蔽一切可能违反数据完整性的操作。

2)CAS(Compare And Set或Compare And Swap)

①概念

CAS是解决多线程并行情况下使用锁造成性能损耗的一种机制。

CAS操作包含三个操作数——内存位置(V),预期原值(A),新值(B)。
如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。
CAS有效地说明了“我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。

②应用

在java中可以通过锁和循环CAS的方式来实现原子操作。

boolean compareAndSet(expectedValue, updateValue);  

当前保持 expectedValue,则以原子方式将变量设置为 updateValue,并在成功时报告 true。

3)举例

独占锁是一种悲观锁。

4)实现

悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

5,公平锁和非公平锁

1)概念

①公平锁

有优先级的锁。加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得。
多个线程在等待同一个锁时,必须按照申请锁的先后顺序来一次获得锁。

②非公平锁

加锁时不考虑排队等待问题,直接尝试获取锁(可抢占,阻塞的线程不会被唤醒),获取不到自动到队尾等待。

2)优缺点

①公平锁的好处是等待锁的线程不会饿死,但是整体效率低;

②非公平锁的好处是整体效率高,但是有些线程可能会饿死。

3)实现

new ReentrantLock(true)实现公平锁,new ReentrantLock(false)实现非公平锁。

7,独占锁

synchronized 是一种独占锁,synchronized 会导致其它所有未持有锁的线程阻塞,而等待持有锁的线程释放锁。

8,阻塞锁

1)概念

阻塞锁,让线程进入阻塞状态进行等待,当获得相应的信号(唤醒,时间) 时,才可以进入线程的准备就绪状态,准备就绪状态的所有线程,通过竞争,进入运行状态。

阻塞锁 被阻塞的线程,不会争夺锁。

2)场景(与自旋锁对比)

阻塞锁的优势在于,阻塞的线程不会占用cpu时间, 不会导致 CPU占用率过高,但进入时间以及恢复时间都要比自旋锁略慢。

在竞争激烈的情况下 阻塞锁的性能要明显高于 自旋锁。
理想的情况则是: 在线程竞争不激烈的情况下,使用自旋锁;竞争激烈的情况下使用,阻塞锁。

3)synchronized

4)ReentrantLock

5)Object.wait()\notify()

6)LockSupport.park()/unpart()(j.u.c经常使用)

9,可重入锁(递归锁)

1)概念

同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。(可多次进入改锁的域)

ReentrantLock 和synchronized都是可重入锁。
具体点击查看

10,读写锁

读写锁是一个资源能够被多个读线程访问,或者被一个写线程访问但不能同时存在读线程。
Java当中的读写锁通过ReentrantReadWriteLock实现。

1)readwritelock

当写操作时,其他线程无法读取或写入数据,而当读操作时,其它线程无法写入数据,但却可以读取数据 。
适用于读取远远大于写入的操作。

2)CopyOnWriteArrayList

①概念

这个类的整个add操作都是在锁的保护下进行的。

CopyOnWriteArrayList使用了一种叫写时复制的方法,当有新元素添加到CopyOnWriteArrayList时,先从原有的数组中拷贝一份出来,然后在新的数组做写操作,写完之后,再将原来的数组引用指向到新数组。

②场景

适合使用在读操作远远大于写操作的场景里,比如缓存。

慎用!
因为谁也没法保证CopyOnWriteArrayList 到底要放置多少数据,万一数据稍微有点多,每次add/set都要重新复制数组,这个代价实在太高昂了。在高性能的互联网应用中,这种操作分分钟引起故障。

③优缺点

A、由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致young gc或者full gc
B、不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求。

11,互斥锁

所谓互斥锁就是指一次最多只能有一个线程持有的锁。在JDK中synchronized和JUC的Lock就是互斥锁。

12,偏向锁

1)概念

jvm控制,可以设置jvm启动参数。
锁会偏向于当前已经占有锁的线程。
无竞争不锁,有竞争挂起,转为轻量锁。

2)场景

竞争激烈的场合,偏向锁会增加系统负担。
没有竞争的场合,所以可以通过偏向来提高性能。

13,类锁和对象锁

1)概念

①类型

在方法上加上static synchronized的锁,或者synchronized(xxx.class)的锁。

②对象锁

锁住对象。

public class LockStrategy{    public Object object1 = new Object();    public static synchronized void method1(){}//类锁    public void method2(){        synchronized(LockStrategy.class){}//类锁    }    public synchronized void method4(){}//对象锁    public void method5()    {        synchronized(this){}//对象锁    }    public void method6()    {        synchronized(object1){}//对象锁    }}

14,线程锁

15,锁粗化

多锁变成一个,为了避免反复加锁解锁,避免频繁的互斥同步操作造成性能损耗,进行了锁范围的扩展。

16,轻量级锁和重量级锁

CAS 实现。
synchronized的偏向锁、轻量级锁以及重量级锁是通过Java对象头实现的。

17,锁消除

1)概念

虚拟机JIT在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。

2)举例

偏向锁就是锁消除的一种。

18,锁膨胀

jvm实现,锁粗化。

19,信号量

使用阻塞锁 实现的一种策略。

20,共享锁(S锁)和排它锁(X锁)

操作系统中对锁的一种定义。java锁不存在X锁和排他锁

1)概念

①共享锁

如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排它锁。获准共享锁的事务只能读数据,不能修改数据。

②排它锁

如果事务T对数据A加上排它锁后,则其他事务不能再对A加任何类型的锁。获得排它锁的事务即能读数据又能修改数据。

原创粉丝点击