func (this *Accout) Write() {
myIdMutex.Lock(this.Id) //写操作锁定
defer myIdMutex.Unlock(this.Id) //写操作解锁
func main() {
acout:=Accout{Id:"798456"}
acout.Reed()
acout.Write()
### 一(对象加锁) 将读写的方法封装,并且添加锁
type Accout struct {
flag sync.Mutex //sync.Mutex类型
//进行读的操作
func (a *Accout) Reed(n int) { //读
a.flag.Lock() //锁上
defer a.flag.Unlock() //在方法运行完之后解开
//进行写的操作
func (a *Accout) Write(n int) { //读
a.flag.Lock() //锁上
defer a.flag.Unlock() //在方法运行完之后解开
## 方法二(直接对数据库进行操作)
### 原理
数据库使用InnoDB
__ InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION);二是采用了行级锁。
__ InnoDB实现了以下两种类型的行锁。
* 共享锁(s):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
* 排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务取得相同的数据集共享读锁和排他写锁。
另外,为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁。
* 意向共享锁(IS):事务打算给数据行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
* 意向排他锁(IX):事务打算给数据行加排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。
__ 如果一个事务请求的锁模式与当前的锁兼容,InnoDB就请求的锁授予该事务;反之,如果两者两者不兼容,该事务就要等待锁释放。
意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及及数据集加排他锁(X)(允许获取排它锁的事物进行操作,其他事物处于阻塞状态);对于普通SELECT语句,InnoDB不会任何锁;事务可以通过以下语句显示给记录集加共享锁或排锁。
共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE
### 代码
package main
import(
"database/sql"
_"github.com/go-sql-driver/mysql"
"log"
"time"
"math/rand"
// 连接池大小
var MAX_POOL_SIZE = 20
var dbPoll chan *sql.DB
const (
user="root"
pass="root"
db="school"
func putDB(db *sql.DB) {
// 基于函数和接口间互不信任原则,这里再判断一次,养成这个好习惯哦
if dbPoll == nil {
dbPoll = make(chan *sql.DB, MAX_POOL_SIZE)
if len(dbPoll) >= MAX_POOL_SIZE {
db.Close()
return
dbPoll <- db
func initDB() {
// 缓冲机制,相当于消息队列
if len(dbPoll) == 0 {
// 如果长度为0,就定义一个redis.Conn类型长度为MAX_POOL_SIZE的channel
dbPoll = make(chan *sql.DB, MAX_POOL_SIZE)
go func() {
for i := 0; i < MAX_POOL_SIZE/2; i++ {
db,err:=sql.Open("mysql",user+":"+pass+"@tcp(localhost:3306)/"+db+"?charset=utf8")
if err!=nil {
log.Println(err)
putDB(db)
func GetDB() *sql.DB {
//如果为空就初始化或者长度为零
if dbPoll == nil||len(dbPoll) == 0{
initDB()
return <- dbPoll
func main(){
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i:=0;i<10 ;i++ {
go changeCount(r.Intn(50))
go changeCount(r.Intn(50))
go changeCount(r.Intn(50))
go changeCount(r.Intn(50))
time.Sleep(3*time.Second)
func changeCount(num int ) {
db:=GetDB()
tx, err := db.Begin()//打开事物
defer tx.Commit()//事物提交
//意向共享锁(IS):事务打算给数据行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
//意向排他锁(IX):事务打算给数据行加排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。
//意向锁是InnoDB自动加的,不需用户干预。
res,_ := tx.Exec("UPDATE product set count=count-? WHERE Id=1 AND count>=? ",num,num)
RowsAffected, err := res.RowsAffected()
if err != nil {
log.Println("res.RowsAffected==================Err")
if RowsAffected>0 {
addToOrder()
log.Println(time.Now(),"抢购成功==================",num)
}else {
log.Println(time.Now(),"抢购失败==================",num)
//添加到订单等操作
func addToOrder() {
## 方法三(中间使用redis进行缓存)
原理可以参考[奔跑的Man这篇博客](https://link.jianshu.com?t=https%3A%2F%2Fwww.cnblogs.com%2Fiforever%2Fp%2F5796902.html)
或者我复制他的Redis多并发问题
import (
"github.com/garyburd/redigo/redis"
"fmt"
"time"
"strconv"
"runtime"
var Address = "127.0.0.1:6379"
var Network = "tcp"
func GetRedis() redis.Conn {
c, err := redis.Dial(Network, Address)
if err != nil {
return GetRedis()
return c
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
for i:=0;i<100;i++ {
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
go do()
time.Sleep(time.Second*60*10)
func do() {
cnn:=GetRedis()
defer cnn.Close()
redisLock("lock.foo",cnn,20,doFunc,"致远")
//lockKey锁的名称
//cnn redis.Conn
//deadTime 锁默认消亡时间
//doFunc 参数名称
//param 方法参数
func redisLock(lockKey string,cnn redis.Conn,deadTime int,doFunc func(interface{}),param interface{}) {
setnxTime:=time.Now().UTC().UnixNano()
ex,err:=cnn.Do("SETNX",lockKey,setnxTime+int64(deadTime))
if err==nil {
if ex==int64(0) {
//fmt.Println("存在锁:下来判断锁是否过期了")
lock2,err:=cnn.Do("GET",lockKey)
if lock2==nil {
//fmt.Println("lock2=======为空ex",ex,ex==int64(0))
redisLock(lockKey ,cnn ,deadTime ,doFunc ,param )
return
if err!=nil {
redisLock(lockKey ,cnn ,deadTime ,doFunc ,param )
return
getTime, err :=strconv.ParseInt(string(lock2.([]uint8)), 10, 64)
if getTime>setnxTime {
//锁未过期
//fmt.Println("锁没有过期:继续等吧")
redisLock(lockKey ,cnn ,deadTime ,doFunc ,param )
return
}else {
//锁已经过期
time.Sleep(time.Millisecond*time.Duration(deadTime))//线程休眠
getsettime:=time.Now().UTC().UnixNano()
lock3,err:=cnn.Do("GETSET",lockKey,getsettime)
if lock3==nil {
//fmt.Println("lock3=======为空")
redisLock(lockKey ,cnn ,deadTime ,doFunc ,param )
return
getSetTime, err :=strconv.ParseInt(string(lock3.([]uint8)), 10, 64)
if err!=nil {
//fmt.Println("出问题了:去继续等吧")
redisLock(lockKey ,cnn ,deadTime ,doFunc ,param )
return
if getSetTime==getTime {//如果更改前的时间和已经过期的时间相同
//获得锁直接操作数据
//fmt.Println("锁过期:处理了死锁,可以直接操作数据")
doFunc(param)
cnn.Do("DEL",lockKey)//删除锁
return
}else{//更改前的时间和已经过期的时间不同
//fmt.Println("判断后:没有死锁,继续等吧")
redisLock(lockKey ,cnn ,deadTime ,doFunc ,param )
return
}else{
//fmt.Println("不存在锁:可以操作数据")
doFunc(param)
cnn.Do("DEL",lockKey)//删除锁
return
}else {
redisLock(lockKey ,cnn ,deadTime ,doFunc ,param )
return
var count=0
func doFunc(str interface{}) {
count+=1
fmt.Println("操作数据中.............============================",count,str)
return