一,Lock
Lock 在 J.U.C 中是最核心的组件。
1,Lock实现
Lock 本质上是一个接口,它定义了释放锁和获得锁的抽象方法,定义成接口就意味着它定义了锁的一个标准规范,也同时意味着锁的不同实现。实现 Lock 接口的类有很多,以下为几个常见的锁实现。
ReentrantLock:表示重入锁,它是唯一一个实现了 Lock 接口的类。重入锁指的是线程在获得锁之后,再次获取该锁不需要阻塞,而是直接关联一次计数器增加重入次数。
ReentrantReadWriteLock:重入读写锁,它实现了 ReadWriteLock 接口,在这个类中维护了两个锁,一个是 ReadLock,一个是 WriteLock,他们都分别实现了 Lock接口。读写锁是一种适合读多写少的场景下解决线程安全问题的工具,基本原则是: 读和读不互斥、读和写互斥、写和写互斥。也就是说涉及到影响数据变化的操作都会存在互斥。
StampedLock: stampedLock 是 JDK8 引入的新的锁机制,可以简单认为是读写锁的一个改进版本,读写锁虽然通过分离读和写的功能使得读和读之间可以完全并发,但是读和写是有冲突的,如果大量的读线程存在,可能会引起写线程的饥饿。stampedLock 是一种乐观的读策略,使得乐观锁完全不会阻塞写线程。
2,Lock 的类关系图
Lock 有很多的锁的实现,但是直观的实现是 ReentrantLock 重入锁

void lock() // 如果锁可用就获得锁,如果锁不可用就阻塞直到锁释放
void lockInterruptibly() // 和lock()方法相似, 但阻塞的线程 可 中 断 , 抛 出java.lang.InterruptedException 异常
boolean tryLock() // 非阻塞获取锁;尝试获取锁,如果成功返回 true
boolean tryLock(long timeout, TimeUnit timeUnit) //带有超时时间的获取锁方法
void unlock() // 释放锁
二,ReentrantLock 重入锁
重入锁,表示支持重新进入的锁,也就是说,如果当前线程 t1 通过调用 lock 方法获取了锁之后,再次调用 lock,是不会再阻塞去获取锁的,直接增加重试次数就行了。synchronized 和 ReentrantLock 都是可重入锁。
1,重入锁的设计目的
比如调用 demo 方法获得了当前的对象锁,然后在这个方法中再去调用demo2,demo2 中的存在同一个实例锁,这个时候当前线程会因为无法获得demo2 的对象锁而阻塞,就会产生死锁。重入锁的设计目的是避免线程的死锁。
public class ReentrantDemo {
public synchronized void demo() {
System.out.println("begin:demo");
demo2();
}
public void demo2() {
System.out.println("begin:demo1");
synchronized (this) {
}
}
public static void main(String[] args) {
ReentrantDemo rd = new ReentrantDemo();
new Thread(rd::demo).start();
}
}
2,ReentrantLock 使用案例
public class AtomicDemo {
private static int count = 0;
static Lock lock = new ReentrantLock();
public static void inc() {
lock.lock();
try {
Thread.sleep(1);
count++;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
new Thread(() -> AtomicDemo.inc()).start();
}
Thread.sleep(3000);
System.out.println("result:" + count);
}
}
3,ReentrantReadWriteLock
以前理解的锁,基本都是排他锁,也就是这些锁在同一时刻只允许一个线程进行访问,而读写所在同一时刻可以允许多个线程访问,但是在写线程访问时,所有的读线程和其他写线程都会被阻塞。读写锁维护了一对锁,一个读锁、一个写锁;一般情况下,读写锁的性能都会比排它锁好,因为大多数场景读是多于写的。在读多于写的情况下,读写锁能够提供比排它锁更好的并发性和吞吐量.
public class LockDemo {
static Map<String, Object> cacheMap = new HashMap<>();
static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
static Lock read = rwl.readLock();
static Lock write = rwl.writeLock();
public static final Object get(String key) {
System.out.println(" 开始读取数据");
read.lock(); // 读锁
try {
return cacheMap.get(key);
} finally {
read.unlock();
}
}
public static final Object put(String key, Object value) {
System.out.println(" 开始写数据");
write.lock();
try {
return cacheMap.put(key, value);
} finally {
write.unlock();
}
}
}
在这个案例中,通过 hashmap 来模拟了一个内存缓存,然后使用读写锁来保证这个内存缓存的线程安全性。当执行读操作的时候,需要获取读锁,在并发访问的时候,读锁不会被阻塞,因

最低0.47元/天 解锁文章
764

被折叠的 条评论
为什么被折叠?



