【java学习】java锁,
【java学习】java锁,
1,概念
1)Java的线程是映射
Java的线程是映射到操作系统的原生线程之上的,如果要阻塞或唤醒一个线程,都需要操作系统来帮忙完成,这就需要从用户态转换到核心态中,因此状态装换需要耗费很多的处理器时间,对于代码简单的同步块(如被synchronized修饰的getter()和setter()方法),状态转换消耗的时间有可能比用户代码执行的时间还要长。
2)饥饿、活锁、死锁
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)概念
同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。(可多次进入改锁的域)
2)synchronized
3)重入锁(ReentrantLock)
reentrantlock要实现线程间的通信,必须创建条件锁。
借助于Condition对象,RetrantLock可以实现类似于Object的wait和notify/notifyAll功能。使用它具有更好的灵活性,在一个Lock对象里面可以创建多个Condition(对象监视器)实例,线程对象可以注册在指定的Condition对象中,从而可以有选择性的进行线程通知,实现多路通知功能,在调度线程上更加灵活。
10,读写锁
读写锁是一个资源能够被多个读线程访问,或者被一个写线程访问但不能同时存在读线程。
Java当中的读写锁通过ReentrantReadWriteLock实现。
1)readwritelock
当写操作时,其他线程无法读取或写入数据,而当读操作时,其它线程无法写入数据,但却可以读取数据 。
适用于读取远远大于写入的操作。
2)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加任何类型的锁。获得排它锁的事务即能读数据又能修改数据。
相关文章
- 暂无相关文章
用户点评