悲观锁代码java_悲观锁的代码实现如何编写?

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

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

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

具体实现代码:

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常见问答知识,烦请持续关注我们的网站吧。

推荐阅读:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
悲观和乐观都是用于并发控制的技术。 悲观的思想是,在整个数据处理过程中,将数据进行加,防止其他线程对该数据进行修改。因此,其他线程需要等待被释放才能对数据进行修改。悲观的缺点是效率低下,因为它需要占用定资源的时间较长。 乐观的思想是,先假设所有线程都能成功修改数据,并进行修改。当某个线程提交时,先检查该数据是否被其他线程修改,如果未被修改,则提交成功,如果已经被修改,则后提交的线程需要进行重试。乐观的优点是效率高,缺点是可能存在矛盾和并发问题。 实现悲观,可以使用数据库中的行机制,在事务处理期间定某些数据行,防止其他事务对该数据进行修改。实现乐观,可以使用 CAS(Compare and Swap)算法,在对数据进行修改之前,先检查该数据是否已经被其他线程修改,如果该数据值未被修改,则执行修改,否则重试修改操作。 以下是Java中使用乐观代码示例: ```java public class Account { private int balance; private int version; // 存款操作 public void deposit(int amount) { while(true) { int oldVersion = version; int newVersion = oldVersion + 1; int newBalance = balance + amount; if(compareAndSwap(oldVersion, newVersion, newBalance)) { balance = newBalance; version = newVersion; break; } } } // 使用CAS操作原子性进行比较 private boolean compareAndSwap(int oldVersion, int newVersion, int newBalance) { synchronized(this) { if(version == oldVersion) { balance = newBalance; version = newVersion; return true; } return false; } } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值