锁的艺术:Java 中的锁类型与欢乐杂谈

发布时间:2024年01月14日

嗨,大家好,欢迎来到程序猿漠然公众号,我是漠然。

亲爱的代码侠们,今天我们将走进Java的锁世界,探索那些锁定的秘密。在这个过程中,我会尽量让你们在笑声中学习,所以请准备好你的笑脸和耳朵,因为这是一场关于锁的喜剧之旅。

乐观锁:假设世界是美好的

乐观锁适用于更新操作频繁且冲突较少的情况。它通过CAS(Compare And Swap)操作来实现,如果更新成功,则直接进行修改;如果更新失败,则重新尝试。

想象一下,乐观锁就像是一个总是看到世界美好的一面的人。在Java中,这可以通过Atomic类实现,比如AtomicInteger。它会在更新数值时,先假设不会有其他线程来打扰它,然后再去检查一下是不是真的没有人抢它的风头。

import?java.util.concurrent.atomic.AtomicInteger;
public?class?OptimisticLockerDemo?{
????private?AtomicInteger?count?=?new?AtomicInteger(0);
????public?void?addCount()?{
????????int?current;
????????do?{
????????????current?=?count.get();
????????????//?假设没有人修改过计数器
????????}?while?(!count.compareAndSet(current,?current?+?1));
????????//?如果有竞争,这里会重试
????}
}

悲观锁:小心翼翼的守门员

悲观锁适用于读写操作频繁且可能发生冲突的情况。它通过独占锁来确保同一时间只有一个线程可以访问资源。

悲观锁,就像是那个总是担心门外的世界充满了危险的人。在Java中,synchronized关键字和ReentrantLock就是这种锁的代表。它们在给你开门之前,会先确认一下是否有人在外面等着闯进来。

import?java.util.concurrent.locks.Lock;
import?java.util.concurrent.locks.ReentrantLock;
public?class?PessimisticLockerDemo?{
????private?final?Lock?lock?=?new?ReentrantLock();
????public?void?accessResource()?{
????????lock.lock();
????????try?{
????????????//?这里是安全的代码区
????????}?finally?{
????????????lock.unlock();
????????}
????}
}

读写锁:有的放矢的守卫

读写锁(ReadWriteLock)允许多个读线程同时访问资源,但写线程在写入时需要独占访问权。这样,既可以提高读操作的并发性,又可以保证写操作的一致性。

读写锁就像是那个知道如何区分访客的人。在读写锁面前,读取动作是自由的,任何人都可以读,但写入动作则需要得到允许。

import?java.util.concurrent.locks.ReadWriteLock;
import?java.util.concurrent.locks.ReentrantReadWriteLock;
public?class?ReadWriteLockerDemo?{
????private?final?ReadWriteLock?readWriteLock?=?new?ReentrantReadWriteLock();
????public?void?readData()?{
????????readWriteLock.readLock().lock();
????????try?{
????????????//?读操作
????????}?finally?{
????????????readWriteLock.readLock().unlock();
????????}
????}
????public?void?writeData()?{
????????readWriteLock.writeLock().lock();
????????try?{
????????????//?写操作
????????}?finally?{
????????????readWriteLock.writeLock().unlock();
????????}
????}
}

偏向锁:偏心的守候者

偏向锁旨在减少不必要的锁竞争开销。当锁主要被一个线程持有时,启用偏向模式可以提高性能,因为它会避免不必要的锁竞争检测。

偏向锁就像是那个只喜欢某个特定客户的服务员。在偏向锁的管理下,锁会偏爱第一个访问它的线程,就像服务员总是优先服务他最喜欢的顾客一样。

import?java.util.concurrent.atomic.AtomicReference;
public?class?BiasedLockerDemo?{
????private?final?AtomicReference<Thread>?owner?=?new?AtomicReference<>();
????public?void?doWork()?{
????????Thread?currentThread?=?Thread.currentThread();
????????if?(owner.get()?==?null?||?owner.get()?==?currentThread)?{
????????????owner.set(currentThread);
????????????//?偏向锁:当前线程独占锁
????????}?else?{
????????????//?如果有其他线程竞争,则释放锁并重新竞争
????????????owner.compareAndSet(currentThread,?null);
????????????owner.set(currentThread);
????????}
????????//?执行操作
????}
}

轻量级锁:灵活的舞者

轻量级锁适用于竞争不激烈或短暂的锁需求。当没有竞争时,它通过CAS操作避免使用重量级的操作系统互斥量。

轻量级锁就像是那个在舞蹈中轻盈跳跃的舞者。当没有竞争时,它通过CAS操作旋转跳跃,就像是在跳一场只有一个人的华尔兹。

import?java.util.concurrent.atomic.AtomicReference;
public?class?LightweightLockerDemo?{
????private?final?AtomicReference<Thread>?owner?=?new?AtomicReference<>();
????public?void?doWork()?{
????????Thread?currentThread?=?Thread.currentThread();
????????```java
????????if?(owner.get()?==?null)?{
????????????owner.compareAndSet(null,?currentThread);
????????}?else?if?(owner.get()?!=?currentThread)?{
????????????//?轻量级锁:如果锁被其他线程持有,则尝试通过自旋等待
????????????while?(!owner.compareAndSet(currentThread,?currentThread))?{
????????????????//?自旋等待
????????????}
????????}
????????//?执行操作
????}
}

自旋锁:不停转的舞者

自旋锁适用于锁被占用时间非常短的情况。线程在等待锁时不会进入等待状态,而是会循环检查锁的状态,这样可以减少线程状态切换的开销

自旋锁就像是那个在等待时也不会闲着的人。当线程尝试获取锁时,它不会直接放弃,而是会围着锁转来转去,就像是在跳一场不停旋转的探戈。

import?java.util.concurrent.locks.LockSupport;
public?class?SpinningLockerDemo?{
????private?final?AtomicReference<Thread>?owner?=?new?AtomicReference<>();
????public?void?doWork()?{
????????Thread?currentThread?=?Thread.currentThread();
????????while?(!owner.compareAndSet(null,?currentThread))?{
????????????//?自旋等待
????????????LockSupport.parkNanos(1);?//?稍作停顿,然后继续自旋
????????}
????????//?执行操作
????????owner.compareAndSet(currentThread,?null);?//?释放锁
????}
}

分段锁:聪明的物业管理者

分段锁适用于高度并发的场景,通过将数据分成多个段,可以减少锁竞争,提高系统的并发性能。

分段锁就像是那个把大楼分成不同区域,并为每个区域分配一个守门人的物业管理者。这样,每个守门人只负责自己的区域,避免了所有的访客都在大门口挤来挤去。

import?java.util.concurrent.locks.Lock;
import?java.util.concurrent.locks.ReentrantLock;
import?java.util.HashMap;
import?java.util.Map;
public?class?SegmentedLockerDemo?{
????private?final?Map<Integer,?Lock>?locks?=?new?HashMap<>();
????public?void?accessResource(int?segment)?{
????????Lock?lock?=?locks.computeIfAbsent(segment,?k?->?new?ReentrantLock());
????????lock.lock();
????????try?{
????????????//?这里是安全的代码区
????????}?finally?{
????????????lock.unlock();
????????}
????}
}

希望这次关于Java锁的幽默之旅能让你们在笑声中学习到Java中锁的艺术。记住,锁的世界就像是一部复杂的芭蕾舞剧,每个角色都有自己的舞蹈,而我们的任务就是确保每个人都能跳得开心。

今天的分享就到这里,如果觉得对你有帮助,感谢点赞、分享、关注一波,你的认可是我创造的最大动力。

更多内容请关注公众号:程序猿漠然,一个分享有趣后端知识的公众号。

文章来源:https://blog.csdn.net/weixin_40725027/article/details/135563398
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。