Java高并发下锁的分类介绍

it2023-11-29  74

Java中的锁介绍:   公平非公平锁、递归锁、自旋锁、读写锁(互斥锁)

公平锁:

每个线程在获取锁时会查看此锁维护的等待队列,若为空或者当前线程是等待队列中的第一个,就占有锁,否则就加入等待队列 多线程按申请锁的顺序来获取锁,顺序策略(队列先来后到)

非公平锁

上来就直接尝试占有锁,若尝试失败,则采用公平锁的方式 多线程获取锁的顺序并不是按照申请锁的顺序,高并发情况下,可能会造成优先级反转或饥饿现象 ReentrantLock默认非公平,synchronized是一种非公平锁

反转:并不是按照顺序来获取锁,允许加塞 饥饿:线程一直获取不到锁

可重入锁(递归锁),避免了死锁

ReentrantLock/synchronized默认可重入锁 同一个线程外层函数获取锁的时候,在进入内层方法会自动获取锁(在同步方法中调用同步方法会自动获取锁,因为是同一把锁)

public synchronized void method1() { method2(); } public synchronized void method2(){ } Lock lock=new ReentrantLock(); public void M() { lock.lock(); lock.lock(); try { System.out.println(); }finally { lock.unlock(); lock.unlock(); } }

Lock加锁在finally中解锁防止线程抛出异常 锁可以重复加,但注意加锁几次解锁几次(若加多次,关闭次数不对应,编译通过,运行出现阻塞)

自旋锁

尝试获取锁的线程不会立即阻塞,而是采用循环的方式去获取锁

优:循环获取直到成功为止,没有类似的wait阻塞(减少线程上下文切换的消耗) 缺:循环会消耗CPU,若是一直不能获取锁,系统性能会下降

自旋锁的手动实现(CAS+自旋思想)
public class Main { AtomicReference<Thread> atReference=new AtomicReference<>(); public void lock() { Thread t=Thread.currentThread(); //第一个线程直接加锁,后续线程进入此循环 //缺点表现:若加锁线程执行时间过长,其余线程一直循环消耗CPU while(!atReference.compareAndSet(null, t)) { } } public void unlock() { Thread t=Thread.currentThread(); atReference.compareAndSet(t,null); }
读锁(共享锁)

该锁可被多个线程所持有,提高了并发性

写锁(独占锁)

该锁只能被一个线程所持有 (ReentrantLock、synchronized)

ReentrantReadWriteLock读锁是共享,写锁是独占 读写锁:读-读能共存,读-写不能共存,写-写不能共存

读写锁(互斥锁)示例:
class Cache{ private volatile Map<String,Object> cache=new HashMap<>(); private ReentrantReadWriteLock lock=new ReentrantReadWriteLock(); public boolean set(String key,Object value) { lock.writeLock().lock(); try { cache.put(key, value); }finally { lock.writeLock().unlock(); } return true; } public Object get(String key){ Object value=null; lock.readLock().lock(); try { value=cache.get(key); }finally { lock.readLock().unlock(); } return value; } }

总结:synchronized是重锁, 并发性低,要有读写分离的思想

最新回复(0)