关于Java锁的一些知识

关于Java锁的一些知识,第1张

关于Java锁的一些知识 1.自旋锁和非自旋锁 2.自旋锁的好处

阻塞和唤醒线程都是需要高昂的开销的,如果同步代码块中的内容不复杂,那么可能转换线程带来的开销比实际业务代码执行的开销还要大。

在很多场景下,可能我们的同步代码块的内容并不多,所以需要的执行时间也很短,如果我们仅仅为了这点时间就去切换线程状态,那么其实不如让线程不切换状态,而是让它自旋地尝试获取锁,等待其他线程释放锁,有时只需要稍等一下,就可以避免上下文切换等开销,提高了效率。

自旋锁的好处,就是自旋锁用循环去不停地尝试获取锁,让线程始终处于 Runnable 状态,节省了线程状态切换带来的开销。

3.自旋锁的缺点

虽然避免了线程切换的开销,但是它在避免线程切换开销的同时也带来了新的开销,因为它需要不停得去尝试获取锁。如果这把锁一直不能被释放,那么这种尝试只是无用的尝试,会白白浪费处理器资源。也就是说,虽然一开始自旋锁的开销低于线程切换,但是随着时间的增加,这种开销也是水涨船高,后期甚至会超过线程切换的开销,得不偿失。

4.自旋锁适用场景

自旋锁适用于并发度不是特别高的场景,以及临界区比较短小的情况,这样我们可以利用避免线程切换来提高效率。

可是如果临界区很大,线程一旦拿到锁,很久才会释放的话,那就不合适用自旋锁,因为自旋会一直占用 CPU 却无法拿到锁,白白消耗资源。

5.synchronized和volatile区别

synchronized可以保证事件的原子性和可见性。volatile只能保证事件的可见性。 6.锁升级机制

偏向锁

线程来之后不加锁,记录线程的id。下次再有线程进来的时候,认为这是上次进来的线程,如果还是上次进来的线程就继续运行不考虑加锁。如果是新的线程不是原来的线程就进行一个锁升级。

自旋锁

使用场景(执行时间短,线程少,使用自旋锁)当有线程将对象锁住之后,自旋锁在外面不断徘徊判断是否能拿到这个锁,当自旋十次还拿不到,执行锁升级。

重量级锁

使用场景(执行时间长,线程多,使用os锁)经过os进入等待队列里 7.乐观锁

CAS *** 作包含三个 *** 作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何 *** 作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。

CAS 有效地说明了”我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”

ABA问题如何解决
添加版本号或时间戳 8.LongAdder原理

LongAdder内部维护一个Cells数组,每个Cell里面有一个初始值为0的long型变量,在同等并发量的情况下,争夺单个变量的线程会减少,这是变相的减少了争夺共享资源的并发量,另外**多个线程在争夺同一个原子变量时候,如果失败并不是自旋CAS重试,而是尝试获取其他原子变量的锁,**最后当获取当前值时候是把所有变量的值累加后再加上base的值返回的。

与AtomicInteger区别
AtomicInteger采用CAS算法。 9.ReentrantLock

需要手动加锁解锁,可采用公平锁或非公平锁机制;

采用CAS结构。

10.CountDownLatch

这个类使一个线程等待其他线程各自执行完之后再执行;

是通过程序计数器来实现的,计数器的初始值是线程的数量。当每个线程开始执行之后,计数器的值就减1,当计算器的值为0的时候,在等待的线程就可以重新开始工作了。

//参数count为计数值
public CountDownLatch(int count) {  }; 

//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public void await() throws InterruptedException { };   
//和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  
//将count值减1
public void countDown() { };
11.CyclicBarrier

这是一个同步辅助类,它允许一组线程相互等待,直到到达某个公共的屏障点,然后才会继续下一步行动。

//parties:参与线程个数
//barrierAction:最后一个到达线程要做的任务
public CyclicBarrier(int parties);
public CyclicBarrier(int parties, Runnable barrierAction);

public int await() throws InterruptedException, BrokenBarrierException
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException
使用场景:

用于多线程计算数据,最后合并计算结果的场景。

CyclicBarrier与CountDownLatch区别:

CountDownLatch 是一次性的,CyclicBarrier 是可循环利用的CountDownLatch 参与的线程的职责是不一样的,有的在倒计时,有的在等待倒计时结束。CyclicBarrier 参与的线程职责是一样的。 12.ReadWriteLock

当读数据时为共享锁,当写数据时为互斥锁。

13.LockSupport

LockSupport是一个线程阻塞的工具类,它的所有方法都是静态方法。可以在线程的任意位置阻塞。

//阻塞当前线程
public static void park();
//唤醒线程
public static void unpark(Thread thread);

与notify/wait 区别在于:notify只能随机选择一个线程唤醒,无法唤醒指定的线程,unpark却可以唤醒一个指定的线程。

14.可重入锁

线程拿到一把锁之后,可以自由进入同一把锁所同步的其他代码。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/zaji/5707562.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-12-17
下一篇2022-12-17

发表评论

登录后才能评论

评论列表(0条)

    保存