锁分为Synchronized和锁Lock。
ReentrantLock与锁
ReentrantLock(可重入锁)与Synchronized(同步锁)的区别:
- 可重入性:区别不大,都是通过计数器,当计数器下降为0时,锁释放。
- 锁的实现:Synchronized依赖JVM,ReentrantLock基于JDK实现。(类似Synchronized是操作系统实现,ReentrantLock是敲代码实现),ReentrantLock可通过看源码明白实现,Synchronized依赖JVM就看不到实现原理。
- 性能的区别:优化之前Synchronized比ReentrantLock性能差很多,优化后Synchronized引入偏向锁、轻量级锁也就是自旋锁后,2者性能差不多了。差不多时,官方推荐Synchronized。
功能的区别:Synchronized的使用方便简洁,隐式获取锁释放锁,ReentrantLock需要手动获取锁,释放锁,如果忘记容易出现死锁,所以最好再finally中释放锁。锁的颗粒度和灵活度很明显ReentrantLock优于Synchronized。
ReentrantLock独有的功能:
- ReentrantLock可指定公平锁、非公平锁,而Synchronized只能时非公平锁。公平锁:先等待的先获取锁,反之亦然。
- 提供了一个Condition类,可以分组唤醒需要唤醒的线程,Synchronized要么随机唤醒,要么全部唤醒。
- 提供能够中断等待锁的线程的机制,lock.lockInterruptibly(),lockInterruptibly是一种自旋锁,通过循环来调用CAS来加锁,性能比较好,就是因为不会进入内核阻塞状态。
使用场景:必须实现上面3个功能时,就必须用了。
代码示例一
ReentrantLock实现累加,注意与synchronized的不用用法
package com.yanxizhu.demo.concurrency.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @description: ReentrantLock锁实现计数,注意使用synchroized怎么实现的
* @author: <a href="mailto:batis@foxmail.com">清风</a>
* @date: 2022/4/25 23:09
* @version: 1.0
*/
@Slf4j
public class ReentrantLockDemo {
//定义锁
private static Lock lock = new ReentrantLock();
//定义锁用户数
private static final int totalThread = 5000;
//定义并发线程数
private static final int concurrentThread = 200;
//定义总和
private static int count = 0;
public static void main(String[] args) throws Exception{
//定义线程池
ExecutorService executorService = Executors.newCachedThreadPool();
//定义信号量
final Semaphore semaphore = new Semaphore(concurrentThread);
//定义闭锁
final CountDownLatch countDownLatch = new CountDownLatch(totalThread);
for (int i = 0; i < totalThread; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
sum();
semaphore.release();
} catch (InterruptedException e) {
log.error("错误信息:【{}】", e.getMessage());
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("最后累加结果Count={}", count);
}
//通过ReentrantLock锁实现
public static void sum() {
lock.lock();
count++;
lock.unlock();
}
//累加求和(synchronized锁实现)
// public synchronized static void sum() {
// count ++;
// }
}
输出结果:
23:21:08.549 [main] INFO com.yanxizhu.demo.concurrency.lock.ReentrantLockDemo - 最后累加结果Count=5000
源码查看
public ReentrantLock() {
sync = new NonfairSync();
}
默认传入的是不公平的锁。
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
可以传入ture或false决定是否是公平锁。
需要注意的方法tryLok:
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
本质是仅在调用的时候,锁定未被另一个线程持有的情况下,才锁定。参数同样可以传入超时时间和单位,保持线程时间多久可以锁定。提供了丰富的方法,可以参考官方文档,
ReentrantReadWriteLock
没有任何读写锁的时候,才可以取得写入锁。可实现悲观读取。读多写少时,可能会导致写进入饥饿。注意是悲观锁,当有读时,写可能一直执行不了,有写的时候,不能读。
代码示例
package com.yanxizhu.demo.concurrency.lock;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @description: ReentrantReadWriteLock 读锁、写锁的使用
* 注意: ReentrantReadWriteLock时悲观锁,读的时候不能写,写的时候不能读,互斥的
* @author: <a href="mailto:batis@foxmail.com">清风</a>
* @date: 2022/4/25 23:40
* @version: 1.0
*/
public class ReentrantReadWriteLockDemo {
//定义读写锁
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
//获取读锁
private static final Lock readLock = lock.readLock();
//获取写锁
private static final Lock writeLock = lock.writeLock();
private static Map<String, Data> map = new TreeMap<>();
public class Data{
}
//读map中的数据
public Data getValue(String key) {
readLock.lock();
try{
return map.get(key);
}catch (Exception e) {
}finally {
readLock.unlock();
}
return null;
}
//读取map中所有的key
public Set<String> getKeys() {
readLock.lock();
try{
return map.keySet();
}catch (Exception e) {
}finally {
readLock.unlock();
}
return null;
}
//向map中写入数据
public void writeDate(String key, Data value) {
writeLock.lock();
try{
map.put(key, value);
}catch (Exception e){
}finally {
writeLock.unlock();
}
}
}
StampedLock
StampedLock控制锁,有3种模式。
写、读、
是由锁和版本控制,就是这里的。
读分为悲观读和乐观读。
官方代码示例
package com.yanxizhu.demo.concurrency.lock;
import java.util.concurrent.locks.StampedLock;
public class StampedLockDemo {
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) { // an exclusively locked method
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
//下面看看乐观读锁案例
double distanceFromOrigin() { // A read-only method
long stamp = sl.tryOptimisticRead(); //获得一个乐观读锁
double currentX = x, currentY = y; //将两个字段读入本地局部变量
if (!sl.validate(stamp)) { //检查发出乐观读锁后同时是否有其他写锁发生?
stamp = sl.readLock(); //如果没有,我们再次获得一个读悲观锁
try {
currentX = x; // 将两个字段读入本地局部变量
currentY = y; // 将两个字段读入本地局部变量
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
//下面是悲观读锁案例
void moveIfAtOrigin(double newX, double newY) { // upgrade
// Could instead start with optimistic, not read mode
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) { //循环,检查当前状态是否符合
long ws = sl.tryConvertToWriteLock(stamp); //将读锁转为写锁
if (ws != 0L) { //这是确认转为写锁是否成功
stamp = ws; //如果成功 替换票据
x = newX; //进行状态改变
y = newY; //进行状态改变
break;
} else { //如果不能成功转换为写锁
sl.unlockRead(stamp); //我们显式释放读锁
stamp = sl.writeLock(); //显式直接进行写锁 然后再通过循环再试
}
}
} finally {
sl.unlock(stamp); //释放读锁或写锁
}
}
}
}
示例代码二
累加求和
package com.yanxizhu.demo.concurrency.lock;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.concurrent.ThreadSafe;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.StampedLock;
@Slf4j
@ThreadSafe
public class StampedLockDemo2 {
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
public static int count = 0;
private final static StampedLock lock = new StampedLock();
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count);
}
private static void add() {
//这里加锁,返回值
long stamp = lock.writeLock();
try {
count++;
} finally {
//释放锁,带上加锁的返回值
lock.unlock(stamp);
}
}
}
输出结果:5000,线程安全,并发性好。
Condition
代码示例
package com.yanxizhu.demo.concurrency.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* ReentrantLock中另一个队列,Condition的使用
*/
@Slf4j
public class ConditionDemo {
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();
new Thread(() -> {
try {
reentrantLock.lock();
log.info("wait signal"); // 1
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("get signal"); // 4
reentrantLock.unlock();
}).start();
new Thread(() -> {
reentrantLock.lock();
log.info("get lock"); // 2
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
condition.signalAll();
log.info("send signal ~ "); // 3
reentrantLock.unlock();
}).start();
}
}
评论 (0)