概念:
线程锁:主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一JVM中有效果,因为线程锁的实现在根本上是依靠线程之间共享内存实现的,比如synchronized是共享对象头,显示锁Lock是共享某个变量(state)。
进程锁:为了控制同一操作系统中多个进程访问某个共享资源,因为进程具有独立性,各个进程无法访问其他进程的资源,因此无法通过synchronized等线程锁实现进程锁。
分布式锁:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。
Redis实现分布式锁的原理:
1.通过setnx(lock_timeout)实现,如果设置了锁返回1, 已经有值没有设置成功返回0
2.死锁问题:通过实践来判断是否过期,如果已经过期,获取到过期时间get(lockKey),然后getset(lock_timeout)判断是否和get相同,
相同则证明已经加锁成功,因为可能导致多线程同时执行getset(lock_timeout)方法,这可能导致多线程都只需getset后,对于判断加锁成功的线程, 再加expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS)过期时间,防止多个线程同时叠加时间,导致锁时效时间翻倍
(坑)针对集群服务器时间不一致问题,可以调用redis的time()获取当前时间
流程图:
代码:
/**
* @author yaoxin
* @date 2018/8/13下午5:04
*/
public class RedisLockTest {
public static final String url = "jdbc:mysql://127.0.0.1:3306/ly?characterEncoding=UTF-8";
public static final String name = "com.mysql.jdbc.Driver";
public static final String user = "root";
public static final String password = "";
public static void main(String[] args) {
Integer count = 50;
while (count > 0) {
count--;
new Thread(new Runnable() {
@Override
public void run() {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.auth("1234");
String lock = lock(jedis);
if (lock != null) {
Statement statement = null;
Connection conn = null;
ResultSet resultSet = null;
try {
Class.forName(name);// 指定连接类型
conn = DriverManager.getConnection(url, user, password);// 获取连接
statement = conn.createStatement();// 准备执行语句
String querySql = "SELECT id,name,count FROM production WHERE id=2";
resultSet = statement.executeQuery(querySql);
int count = 0;
while (resultSet.next()) {
System.out.println(Thread.currentThread().getName() + "抢到了锁 id: " + resultSet.getString("id")
+ " name: " + resultSet.getString("name")
+ " count: " + resultSet.getString("count"));
count = Integer.valueOf(resultSet.getString("count"));
}
String updateSql = "UPDATE production SET count=" + (count - 1)
+ " WHERE id=2";
int rows = statement.executeUpdate(updateSql);
if (rows > 0) {
System.out.println("更新成功" + Thread.currentThread().getName() + " 库存剩余:" + (count - 1));
System.out.println(Thread.currentThread().getName() + " === > >开始解锁");
boolean unlock = unlock(jedis, lock);
if (unlock)
System.out.println(Thread.currentThread().getName() + " === > >解锁成功");
} else {
System.out.println("更新失败" + Thread.currentThread().getName());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (conn != null)
conn.close();
if (statement != null)
statement.close();
if (resultSet != null)
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}, "线程" + count).start();
}
}
public static String lock(Jedis jedis) {
try {
while (true) {
String lockTime = Long.valueOf(jedis.time().get(0)) + 5 + "";
if (jedis.setnx("lock", lockTime) == 1) {
jedis.expire("lock", 5);
return lockTime;
}
String lock = jedis.get("lock");
if (!StringUtils.isEmpty(lock) && Long.valueOf(lock) < Long.valueOf(jedis.time().get(0))) {
String oldLockTime = jedis.getSet("lock", lockTime);
if (!StringUtils.isEmpty(oldLockTime) && oldLockTime.equals(lock)) {
return lockTime;
}
}
Thread.sleep(100);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static boolean unlock(Jedis jedis, String lockTag) {
if (lockTag.equals(jedis.get("lock"))) {
jedis.del("lock");
return true;
}
return false;
}
}
运行结果如下图: