锁的分类
读写锁,非公平锁,可重入锁,自旋锁,乐观锁悲观锁,死锁
读写锁
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();//volatile保证可见性
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
public void put(String key, Object value) {
rwLock.writeLock().lock();//写锁创建
try {
System.out.println(Thread.currentThread().getName() + "\t 正在写入:" + key);
try {
// 模拟网络拥堵,延迟0.3秒
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "\t 写入完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.writeLock().unlock();//写锁释放
}
}
public void get(String key) {
rwLock.readLock().lock();//读锁创建
try {
System.out.println(Thread.currentThread().getName() + "\t 正在读取:");
try {
TimeUnit.MILLISECONDS.sleep(300);//模拟网络拥堵,延迟0.3秒
} catch (InterruptedException e) {
e.printStackTrace();
}
Object value = map.get(key);
System.out.println(Thread.currentThread().getName() + "\t 读取完成:" + value);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.readLock().unlock();//读锁释放
}
}
public void clean() {
map.clear();//清空缓存
}
}
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 1; i <= 5; i++) {//5个线程写
final int tempInt = i;//final
new Thread(() -> {
myCache.put(tempInt + "", tempInt + "");
}, String.valueOf(i)).start();
}
for (int i = 1; i <= 5; i++) {//5个线程读
final int tempInt = i;//final
new Thread(() -> {
myCache.get(tempInt + "");
}, String.valueOf(i)).start();
}
}
}
非公平锁(synchronized/ReentrantLock)
名字 | 定义 | 区别 |
非公平锁 | 多个线程获取锁的顺序,并不是按照申请锁的顺序,有可能申请的线程比先申请的线程优先获取到锁,在高并发的环境下,有可能造成优先翻转,或者饥饿的线程(也就是某个线程一直获取不到锁) | 比较粗鲁,上来就直接尝试占有锁,如果失败,就采用公平锁的方式 |
公平锁 | 多个线程按照申请锁的顺序来获取锁,队列,先进先出先进先获取,这就是公平的 | 很公平在并发的环境下,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列中的第一个,就占用锁,否者就会加入到等待队列中,以后安装FIFO的规则从队列中取到自己 |
- synchronized是非公平锁
- ReentrantLock默认非公平锁
-
- Lock lock = new ReentrantLock(true);//默认false非公平锁,true公平锁
可重入锁/递归锁(synchronized/ReentrantLock)
class MySynchronized {
public synchronized void sendSMS() throws Exception{//发短信
System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS()");
sendEmail();//同步方法中调用另外一个同步方法
}
public synchronized void sendEmail() throws Exception{//发邮件
System.out.println(Thread.currentThread().getId() + "\t invoked sendEmail()");
}
}
public class MyDemo {
public static void main(String[] args) {
MySynchronized mySynchronized = new MySynchronized();
new Thread(() -> {
try {
mySynchronized.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "t1").start();
new Thread(() -> {
try {
mySynchronized.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "t2").start();
}
}
/**
t1 invoked sendSMS()
t1 invoked sendEmail()
t2 invoked sendSMS()
t2 invoked sendEmail()
*/
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyReentrantLock implements Runnable{
Lock lock = new ReentrantLock();
@Override
public void run() {
method1();
}
public void method1() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()
+ "\t exe method1");
method2();
} finally {
lock.unlock();
}
}
public void method2() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()
+ "\t exe method2");
} finally {
lock.unlock();
}
}
}
public class ReenterLockDemo {
public static void main(String[] args) {
MyReentrantLock myReentrantLock = new MyReentrantLock();
Thread t1 = new Thread(myReentrantLock, "t1");
Thread t2 = new Thread(myReentrantLock, "t2");
t1.start();
t2.start();
}
}
/**
t1 exe method1
t1 exe method2
t2 exe method1
t2 exe method2
*/
自旋锁(spinlock)
public class SpinLockDemo {
// 现在的泛型装的是Thread,原子引用线程
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock() {//加锁
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "\t come in ");
//开始自旋,期望值是null,更新值是当前线程,如果是null,则更新为当前线程,否者自旋
while(!atomicReference.compareAndSet(null, thread)) {
}
}
public void myUnLock() {//解锁
Thread thread = Thread.currentThread();
//自己用完了后,把atomicReference变成null
atomicReference.compareAndSet(thread, null);
System.out.println(Thread.currentThread().getName() + "\t invoked myUnlock()");
}
public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(() -> {
spinLockDemo.myLock();//加锁
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.myUnLock();//释放锁
}, "t1").start();
//1秒后,启动t2线程占用锁
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
spinLockDemo.myLock();//加锁
spinLockDemo.myUnLock();//释放锁
}, "t2").start();
}
}
/**
t1 come in
.....五秒后.....
t1 invoked myUnlock()
t2 come in
t2 invoked myUnlock()
*/
乐观锁/悲观锁
MybatisPlus使用乐观锁的3步走
step1、在数据库增加version字段,默认为1
step2、在实体类增加对应的字段
@Version
private Integer version;
step3、注册乐观锁,在MybatisPlusConfig中配置
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
死锁
import java.util.concurrent.TimeUnit;
class HoldLockThread implements Runnable{
private String lockA;
private String lockB;
public HoldLockThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA) {
System.out.println(Thread.currentThread().getName()
+ "\t 自己持有" + lockA + "\t 尝试获取:" + lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB) {
System.out.println(Thread.currentThread().getName()
+ "\t 自己持有" + lockB + "\t 尝试获取:" + lockA);
}
}
}
}
public class DeadLockDemo {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new HoldLockThread(lockA, lockB), "t1").start();
new Thread(new HoldLockThread(lockB, lockA), "t2").start();
}
}
/**
t1 自己持有lockA 尝试获取:lockB
t2 自己持有lockB 尝试获取:lockA
*/
sychronized与lock的对比
sychronized | lock | |
1.定义 | JVM层面的java关键字,底层是通过monitor对象来完成 | api层面的锁,底层是JUC锁(java.util.concurrent.locks.Lock) |
2.使用方法 | 不需要用户去手动释放锁,系统自动释放 | 需要用户去手动释放锁,若没有主动释放锁,就有可能出现死锁的现象,需要lock() 和 unlock() 配置try catch语句来完成 |
3.等待是否中断 | 不可中断 |
|
4.加锁是否公平 | 非公平锁 | 默认非公平锁,构造函数可以传递boolean值,true为公平锁,false为非公平锁 |
锁绑定多个条件Condition | 没有,要么随机,要么全部唤醒 | 可以精确唤醒 |