Java悲观锁Pessimistic-Lock常用实现场景

1:商品库存秒杀采用悲观锁Pessimistic-Lock主要好处是安全,充分利用了数据库的性能来做的一种锁机制。

悲观锁的实现: 

(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<Map<String,Object>> list = jdbcTemplate.queryForList(sql);
        if(null != list && list.size() > 0){
            Map<String,Object> 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 < STOCK; 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)模拟10000个用户抢购9999个商品,最后一位用户没有抢到:

当前库存值: 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.

 

3:悲观锁demo地址

 

 

 

    原文作者:yugenhai
    原文地址: https://www.cnblogs.com/KuJo/p/10047996.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞