悲观锁相信大家都不陌生了,之前说过了悲观锁的各种原理,这次我们就来聊聊悲观锁是如何实现的吧。

悲观锁,就跟它的名字意思一样,它非常悲观,它的机制就是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,所以,在全部的数据处理过程中,它会把数据处于锁定状态。悲观锁的实现,会依赖数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

一段执行逻辑加上悲观锁,不同线程同时执行时,只能有一个线程执行,其他的线程在入口处等待,直到锁被释放。

具体实现代码:

1)环境:mysql + jdbctemplate

2)商品表 goods:DROP TABLE IF EXISTS `goods`;

CREATE TABLE `goods`(

`id`

int(11) unsigned NOT NULL AUTO_INCREMENT

, `name`

varchar(100) DEFAULT NULL COMMENT '商品名称'

, `stock`

int(11) unsigned NOT NULL COMMENT '商品库存'

, `version`

int(2) DEFAULT NULL COMMENT '版本号'

, `token_time`

datetime NOT NULL COMMENT '乐观锁时间戳'

, PRIMARY KEY(`id`)

) ENGINE = InnoDB AUTO_INCREMENT = 10 DEFAULT CHARSET = utf8;

-- -- -- -- -- -- -- -- -- -- -- -- -- -- --

--Records of goods

-- -- -- -- -- -- -- -- -- -- -- -- -- -- --

BEGIN;

INSERT INTO `goods`

VALUES(1, 'product', 9999, 1, '2018-11-30 22:06:20');

COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

3)DAO层代码package org.yugh.goodsstock.pessimistic_lock.repository;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

import java.util.List;

import java.util.Map;

/**

* @author: YuGenHai

* @name: SeckRepository

* @creation: 2018/11/28 00:30

* @notes: 悲观锁DAO层

* @notes: 悲观锁需要注意mysql自带自动commit,用行锁需要开启事务 set transation 或者set autocommit =0

* 防止自动提交,set autocommit =1 自动提交

*/

@Repository

public class PessimisticLockRepository

{

/**

* 测试使用 {@link JdbcTemplate}

*/

@Resource

private JdbcTemplate jdbcTemplate;

/**

* 获取现有库存量

* @param id

* @return

* @author yugenhai

*/

public int queryStock(long id)

{

//开启事务

String lock = "set autocommit=0";

jdbcTemplate.update(lock);

//获得当前库存 并上锁

String sql = "select * from goods where id=1 for update";

List > list = jdbcTemplate.queryForList(sql);

if (null != list && list.size() > 0)

{

Map  map = list.get(0);

System.out.println("当前库存值: " + map.get("stock"));

return Integer.valueOf(String.valueOf(map.get("stock")));

}

return 0;

}

/**

* 还有库存量,并且要释放当前锁

* @author yugenhai

* @return

*/

public int updateStock()

{

String update = "update goods set stock=stock-1 where id=1";

jdbcTemplate.update(update);

String unlock = "commit";

jdbcTemplate.update(unlock);

return 1;

}

/**

* 商品被抢光后需要释放

* @author yugenhai

* @return

*/

public int unlock()

{

String unlock = "commit";

jdbcTemplate.update(unlock);

return 1;

}

}

4)悲观锁测试:package org.yugh.goodsstock.pessimistic_lock;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.test.context.junit4.SpringRunner;

import org.yugh.goodsstock.pessimistic_lock.repository.PessimisticLockRepository;

import javax.annotation.Resource;

/**

* @author: YuGenHai

* @name: PessimisticLockTest

* @creation: 2018/11/28 00:32

* @notes: 悲观锁测试秒杀商品

*/

@RunWith(SpringRunner.class)

@SpringBootTest

public class PessimisticLockTest

{

@Resource

PessimisticLockRepository pessimisticLockRepository;

/**

* STOCK库存总数,测试可以理解为购买者

* 表里的stock对应库存

*/

private static final int STOCK = 10000;

/**

* 悲观锁秒杀商品

* @author yugenhai

*/

@Test

public void pessimisticLockTest()

{

long beTime = System.currentTimeMillis();

for (int i = 0; i

{

//获得当前库存

//顺带上锁,开启事务

int stock = pessimisticLockRepository.queryStock(1);

if (stock > 0)

{

//库存还有

//当前用户继续秒杀一个商品 并提交事务 释放锁

pessimisticLockRepository.updateStock();

System.out.println(new Thread()

.getName() + " 抢到了第 " + (i + 1) + " 商品");

}

else

{

//没有库存后释放锁

System.err.println(new Thread()

.getName() + " 抱歉,商品没有库存了!");

pessimisticLockRepository.unlock();

//break;

}

}

System.out.println("秒杀 " + STOCK + " 件商品使用悲观锁需要花费时间:" + (System.currentTimeMillis() - beTime));

}

}

5)模拟一万用户在抢购,最后只有一位用户没有抢到:当前库存值: 8

Thread - 9994 抢到了第 9992 商品

当前库存值: 7

Thread - 9995 抢到了第 9993 商品

当前库存值: 6

Thread - 9996 抢到了第 9994 商品

当前库存值: 5

Thread - 9997 抢到了第 9995 商品

当前库存值: 4

Thread - 9998 抢到了第 9996 商品

当前库存值: 3

Thread - 9999 抢到了第 9997 商品

当前库存值: 2

Thread - 10000 抢到了第 9998 商品

当前库存值: 1

Thread - 10001 抢到了第 9999 商品

当前库存值: 0

秒杀 10000 件商品使用悲观锁需要花费时间: 9922

Thread - 10002 抱歉, 商品没有库存了!

2018 - 12 - 01 00: 51: 06.914 INFO 9125-- - [Thread - 2] s.c.a.AnnotationConfigApplicationContext: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext @f0da945: startup date[Sat Dec 01 00: 50: 56 CST 2018];

root of context hierarchy

2018 - 12 - 01 00: 51: 06.915 INFO 9125-- - [Thread - 2] com.zaxxer.hikari.HikariDataSource: HikariPool - 1 - Shutdown initiated...

2018 - 12 - 01 00: 51: 06.920 INFO 9125-- - [Thread - 2] com.zaxxer.hikari.HikariDataSource: HikariPool - 1 - Shutdown completed.

以上就是今天的全部内容了,悲观锁一般适合写入操作比较频繁的场景;如若想要了解更多锁机制相关java常见问答知识,烦请持续关注我们的网站吧。

推荐阅读:

悲观锁代码java_悲观锁的代码实现如何编写?相关推荐

  1. 关闭弹窗代码java_弹出窗口代码

    [1.最基本的弹出窗口代码] 其实代码非常简单: 因为着是一段javascripts代码,所以它们应该放在之间.是对一些版本低的浏览器起作用,在这 些老浏览器中不会将标签中的代码作为文本显示出来.要养 ...

  2. 无锁队列 java_无锁队列的总结

    首次接触无锁数据结构的设计,请各位大佬多多指教~~~ CAS(Compare && Swap)原子操作 CAS是无锁(lock free)的数据结构的基础.用伪代码描述: input: ...

  3. 记事本改字体的代码java_求java记事本代码(带字体设置功能)?

    java中没有自带的字体对话框,这需要自己来编写. text.setFond("字体名字",字形(如,fond.bold),大小) import java.awt.*; impor ...

  4. 贪吃蛇代码java_贪吃蛇 java代码

    展开全部 自己写着玩的,很简单32313133353236313431303231363533e78988e69d8331333262363032,你试一试哦... 主要用了javax.swing.T ...

  5. 详解各种锁:CAS、共享锁、排它锁、互斥锁、悲观锁、乐观锁、行级锁、表级锁、页级锁、死锁、JAVA对CAS的支持、ABA问题、AQS原理

    共享锁(S锁) 又称为读锁,可以查看但无法修改和删除的一种数据锁.如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排它锁.获准共享锁的事务只能读数据,不能修改数据. 共享锁下其它用 ...

  6. 线程调度、公平锁和非公平锁、乐观锁和悲观锁、锁优化、重入锁

    1. 线程调度 线程调度指的就是给线程分配使用处理器的过程.主要的调度方式有两种:协同式调度和抢占式调度. 1.1 协同式调度 线程完成自己的任务之后主动通知系统切换到另一个线程上. 优点: 实现简单 ...

  7. java让线程空转_Java锁:悲观/乐观/阻塞/自旋/公平锁/闭锁,锁消除CAS及synchronized的三种锁级别...

    JAVA LOCK 大全 [TOC] 一.广义分类:乐观锁/悲观锁 1.1 乐观锁的实现CAS (Compare and Swap) 乐观锁适合低并发的情况,在高并发的情况下由于自旋,性能甚至可能悲观 ...

  8. 并发编程中常见的锁机制:乐观锁、悲观锁、CAS、自旋锁、互斥锁、读写锁

    文章目录 乐观锁 VS 悲观锁 悲观锁 乐观锁 CAS CAS机制 ABA问题 CAS的优缺点 互斥锁 VS 自旋锁 互斥锁 自旋锁 对比及应用场景 读写锁 实现方式 读写锁 VS 互斥锁 乐观锁 V ...

  9. [Todo] 乐观悲观锁,自旋互斥锁等等

    乐观锁.悲观锁.要实践 http://chenzhou123520.iteye.com/blog/1860954 <mysql悲观锁总结和实践> http://chenzhou123520 ...

最新文章

  1. Android runOnUiThread() 方法的使用
  2. MATLAB实战系列(十七)-大学生数学建模赛题解析-水塔中水流量估计(附MATLAB源码)
  3. 虚拟化服务器故障切换,虚拟机故障转移
  4. java高并发(二十一)高并发场景下缓存常见问题
  5. C库宏-offsetof()
  6. DB2 乱码
  7. Target “xxx” links to target “Boost::filesystem“ but the target was not found
  8. 认识伪类元素:before和:after
  9. C语言条件判断 if / else - C语言零基础入门教程
  10. C++编程语言中异常处理(try-catch-throw)介绍
  11. Python实现pdf转word
  12. GB35114---基于pjsip协议库开发问题
  13. Java NIO、BIO介绍
  14. 阿里短信验证码接口和支付宝沙箱支付接口
  15. matlab 线性拟合polyfit_matlab如何做线性拟合
  16. [《不敢说爱的年纪》小个子的小说集]2012年8月28日
  17. java duplicate_Duplicate spring bean id
  18. 迪杰斯算法c语言,欧博体育APP-欧博体育APP
  19. Sliding Window Maximum
  20. 用JAVA怎么做个视图_java问题 可以把用JFrame做出来的具有视图层的小程序放在桌面上当一个小软件吗?...

热门文章

  1. SAP Spartacus里product code出现在url的几种场景
  2. Angular里遇到的一个依赖注入问题以及解决办法
  3. Spring JDBC 框架一个最简单的Hello World级别的例子
  4. SAP CRM IBASE保存的逻辑分析
  5. 如何从SAP Fiori Launchpad里找到ABAP Development Tool的下载地址
  6. SAP S/4HANA Material Fiori应用根据扩展字段搜索的实现原理
  7. 在SAP Cloud Platform上消费Business partner service遇到的错误
  8. SAP Gateway 类型为multipart mixed的请求处理逻辑
  9. SAP ui5 jQuery.sap.getModulePath(FioriTest)
  10. React里require('object-assign')里的实现原理