基于Mysql实现锁机制

1 ReentrantLock

它是一个可重入锁,即一个线程可重复加锁,即当前线程获取该锁再次获取不会被阻塞。获得几次锁,解锁也要解几次。
它有两种实现,公平锁和非公平锁。

  • 公平锁:锁的获取顺序就应该符合请求上的绝对时间顺序
  • 非公平锁:不按请求的时间,谁抢到就是谁的

默认实现的为非公平锁,因为公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,而非公平锁会降低一定的上下文切换,降低性能开销。

1
2
//创建一个公平锁
Lock lock = new ReentrantLock(true);

2. 通过MySQL的GET_LOCK函数实现

获得锁

1
GET_LOCK(str,timeout)

根据字符串str 得到一个锁,超时为timeout 秒。若成功得到锁,则返回 1,若操作超时则返回0 ,当它获得锁之后,后面相同str的再获取这把锁就会一直等待,当超过timeout,就会返回0

释放锁

1
RELEASE_LOCK(str);

释放str这把锁,当连接中断后,这把锁也会释放的

使用

1
2
3
4
5
获取锁
SELECT GET_LOCK("pibigstar",10);

释放锁
SELECT RELEASE_LOCK(“pibigstar”);

3 通过MySQL行级锁进行实现

原理很简单,我们插入数据的时候都是只能插入一个唯一主键,如果有一个线程插入了,那么其他线程都不能插入了。

插入实体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.pibigstar.domain;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Mysql {
@Id
private int id;
public Mysql(int id) {
this.id = id;
}
//setter、getter方法

}

dao层:使用jpa

1
2
3
4
5
6
7
8
9
package com.pibigstar.dao;

import org.springframework.data.jpa.repository.JpaRepository;

import com.pibigstar.domain.Mysql;

public interface MysqlLockDao extends JpaRepository<Mysql, Integer>{

}

具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.pibigstar.utils;

import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import org.springframework.beans.factory.annotation.Autowired;
import com.pibigstar.dao.MysqlLockDao;
import com.pibigstar.domain.Mysql;

/**
* 基于mysql实现可重入锁
* @author pibigstar
*
*/
public class MysqlLock implements Lock{

@Autowired
private MysqlLockDao mysqlLockDao;

private static final Mysql my = new Mysql(1);

@Override
public void lock() {
//阻塞锁,不拿到资源不罢休
if (!tryLock()) {
try {
//如果没拿到,睡一下再拿
Thread.sleep(new Random().nextInt(10)+1);
} catch (InterruptedException e) {
}
lock();//继续调用自身拿锁
}
}

@Override
public boolean tryLock() {
//非阻塞锁,拿一次,拿到就拿到了,拿不到就撤
try {
mysqlLockDao.save(my);//插入成功即是拿到了
return true;
} catch (Exception e) {
//有异常说明已经被其他线程拿到了,
return false;
}
}

@Override
public void unlock() {
//解锁,将值删除即可
mysqlLockDao.delete(my);
}

@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}

@Override
public void lockInterruptibly() throws InterruptedException {

}

@Override
public Condition newCondition() {
return null;
}

}
-------------本文结束感谢您的阅读-------------