时间:2021-07-01 10:21:17 帮助过:48人阅读
CREATE TABLE `methodLock` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘主键‘, `method_name` varchar(64) NOT NULL DEFAULT ‘‘ COMMENT ‘锁定的方法名‘, `mydesc` varchar(1024) NOT NULL COMMENT ‘备注信息‘, `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uidx_method_name` (`method_name`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
当我们想要锁住某个方法时,执行以下sql:
insert into methodLock(method_name,desc) values (‘具体方法名‘,‘描述‘);
以上的简单实现有几个问题:
1、这把锁依赖数据库的可用性,如果数据库是一个单点,一旦挂掉,会导致业务系统不可用; 2、这把锁没有失效时间,一旦解锁操作失败,会导致锁一直存留在数据库中,其它线程无法获得锁; 3、这把锁只能是非阻塞的,因为数据的insert操作一旦插入失败就直接报错,没有获得锁的线程不会进入排队队列,想要再次获得锁就要再次触发获得锁的操作; 4、这把锁是非重入的,同一线程在没有释放锁之前无法再次获得该锁,因为表中数据已经存在了。 当然上面的问题也是可以解决的: 1、单点问题,两个数据库,双向同步,一旦挂掉切换到另一个上; 2、失效时间,做一个定时任务,每隔多长时间清理超时数据; 3、非阻塞问题,程序写for循环多次尝试,直至获取到锁为止; 4、非重入,增加一个字段,记录获取所的ip跟线程信息,下次查询的时候如果有,则直接给锁; 示例代码: 获取锁:public boolean lock(){ int result = 0; try { result = jdbcTemplate.update("insert into methodLock(method_name,mydesc)values(?,?)", new Object[]{"com.wzy.home.study.distributedlock.MyResources.getNextId()", "获取orderId"}); }catch (Exception e){ //todo nothing } if(result == 1){ return true; } return false; }
释放锁:
public boolean unLock(){ int rows = jdbcTemplate.update("delete from methodLock where method_name = ?",new Object[]{"com.wzy.home.study.distributedlock.MyResources.getNextId()"}); if(rows == 1){ return true; }else { return false; } }
两个线程,模拟两个客户端进行测试:
public void testLock(){ Thread t1 = new Thread(new Runnable() { @Override public void run() { boolean flag = mysqlLock.lock(); if(flag){//有锁 int orderId = MyResources.getInstance().getNextId(); System.out.println("t1拿到锁,获取的订单id为:"+orderId); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t1释放锁了"); mysqlLock.unLock(); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } tryLock(); } //多次尝试获取锁 private boolean tryLock(){ boolean flag = mysqlLock.lock(); if(flag){ int orderId = MyResources.getInstance().getNextId(); System.out.println("t2拿到锁,获取订单id为:"+orderId); }else { System.out.println("t2获取锁失败,再次尝试"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } tryLock(); mysqlLock.unLock(); } return flag; } }); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } }
输出结果:
t1拿到锁,获取的订单id为:1 t2获取锁失败,再次尝试 t2获取锁失败,再次尝试 t2获取锁失败,再次尝试 t2获取锁失败,再次尝试 t2获取锁失败,再次尝试 t1释放锁了 t2拿到锁,获取订单id为:2 可以看到,的确是t2等t1释放锁后才拿到了锁进行了业务操作。基于数据库的分布式锁
标签:切换 utf8 定时 timestamp system 无法获得 let 定时任务 统一