队友总抢好装备, 互斥模式(Mutex Pattern)来安慰

目的

保护一个资源在同一时间只会有一个资源持有者访问/操作

例子代码

最近好久没玩吃鸡了, 手都生了, 每局都是第二名的我不想过度吹嘘的我刚(fu)枪(di)技能, 最近一局很气, 我带着队友刚到房区, 刚下车听到三个脚步声知道打不过, 开车就跑, 但是倒地的队友挡住了我的车, 导致一起落地成盒了, 有队友好烦呀

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b8jDkveg-1602296325263)(/assets/2020053000.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-obl2PLZF-1602296325264)(/assets/2020053001.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UhQxomGv-1602296325265)(/assets/2020053002.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AoRUXhOw-1602296325266)(/assets/2020053003.png)]

在吃鸡里有一个空投, 里面有三级头三级甲和枪, 当大家一起去拿空投的物资的时候, 会发现明明三级头就在箱子里, 却被干啥啥不行, 舔包第一名的队友抢去, 而你可能只抢到了30 发子弹, 这似乎是个理所应当的事情, 箱子里就一个三级头, 当然只能一个人穿, 但是这其实是个很难的问题, 我们来模拟一下:

先定义一个空投:

@Data
public class AirdropBox {
    //枪的名字
    private String gunName = "GZONE";

    //最近法规必须戴头盔了, 这个翻译是不对的
    private String level3Head = "三级头";

    //三级甲
    private String level3Armor = "三级甲";

    //吉利服可能是这么翻译的
    private String luckClothes = "吉利服";
}

再定义一个玩家类:

@Data
public class Player {
    private String gunName;
    private String level3Head;
    private String level3Armor;
    private String luckClothes;

}

我们定义一个玩家取装备的方法:

@SneakyThrows
public void takeGoodsFromAirDrop(Player player, AirdropBox airdropBox) {
    Thread.sleep(randomMillis());
    player.setGunName(airdropBox.getGunName());

    Thread.sleep(randomMillis());
    player.setLevel3Armor(airdropBox.getLevel3Armor());

    Thread.sleep(randomMillis());
    player.setLevel3Head(airdropBox.getLevel3Head());

    Thread.sleep(randomMillis());
    player.setLuckClothes(airdropBox.getLuckClothes());

    airdropBox.setLevel3Armor(StringUtils.EMPTY);
    airdropBox.setLevel3Head(StringUtils.EMPTY);
    airdropBox.setGunName(StringUtils.EMPTY);
    airdropBox.setLuckClothes(StringUtils.EMPTY);
}

使用多线程为玩家取装备:

AirdropBox airdropBox = new AirdropBox();
Player nbBoy = new Player();
Player ljBoy = new Player();
ExecutorService executorService = Executors.newFixedThreadPool(2);
App app = new App();
Future<?> nbSubmit = executorService.submit(() -> app.takeGoodsFromAirDrop(nbBoy, airdropBox));
Future<?> ljSubmit = executorService.submit(() -> app.takeGoodsFromAirDrop(ljBoy, airdropBox));
while (!nbSubmit.isDone() && !ljSubmit.isDone()) {
}
System.out.println("大神的装备:" + nbBoy.toString());
System.out.println("我的装备:" + ljBoy.toString());
executorService.shutdown();

输出如下:

大神的装备:Player(gunName=GZONE, level3Head=null, level3Armor=三级甲, luckClothes=null)
我的装备:Player(gunName=GZONE, level3Head=三级头, level3Armor=三级甲, luckClothes=吉利服)

问题分析

以往的问题分析都是指扩展性这样的分析, 这就涉及到正确性的分析了, 一个明显的问题是多线程情况下如何保证只有一个线程能访问到这个箱子

互斥模式

public synchronized void takeGoodsFromAirDrop(Player player, AirdropBox airdropBox)

把这个方法加上锁就能解决问题了

更近一步

上面虽然是能够解决多线程情况下一个资源的占用处理, 但是如果是分布式情况下, 一套代码部署到多个服务器, 这个 JVM 层级的锁就不行了, 所以可以使用分布式锁

我们使用模板方法模式(https://mp.weixin.qq.com/s?__biz=MzUzOTQ2MjgwNA==&mid=2247483724&idx=1&sn=eb3b705c449f7b2c6fca7e6417e55918&chksm=fac95cb7cdbed5a1256cf1ccf173609320a637e58658e7fdadba94ada9d4bec806ca53ff0ccf&token=1268878968&lang=zh_CN#rd)和 Callback 模式(https://mp.weixin.qq.com/s?__biz=MzUzOTQ2MjgwNA==&mid=2247483909&idx=1&sn=3ba745565ecd2fd56d66adc7f1ffe2cc&chksm=fac95ffecdbed6e8a05241bc530a92d5c7853f79cf73a5e9a58b5b991e67264cec5117327bd9&token=1268878968&lang=zh_CN#rd)结合:

public interface LockCallback {
    //判断业务是否是正确的状态, 防重等
    boolean checkStatus(String uuid);

    //执行业务方法
    void execute();
}

定义一个模板:

public class LockExecuteTemplate {

    @Autowired
    private CuratorFramework lockClient;

    public void handLockProcess(LockCallback lockCallback, BusinessTypeLockEnum businessTypeLockEnum, String uuid) {
        InterProcessLock lock = new InterProcessMutex(lockClient, businessTypeLockEnum + uuid);
        try {
            Preconditions.checkArgument(lock.acquire(2000, TimeUnit.SECONDS));
        } catch (Exception e) {
            log.error("acquire lock failed",e);
            throw new RuntimeException("acquire lock failed", e);
        }
        try {
            boolean isRightStatus = lockCallback.checkStatus(uuid);
            if(isRightStatus) {
                lockCallback.execute();
            }
        } catch (Exception e) {
            log.error("execute lock failed",e);
            throw new RuntimeException("execute lock failed", e);
        } finally {
            try {
                lock.release();
            } catch (Exception e) {
                log.error("release lock failed",e);
                throw new RuntimeException("release lock failed", e);
            }
        }

    }
}

lockClient 是使用 zookeeper 实现的分布式锁, lockCallback 是定义的接口, 用户可以传入不同的实现, 业务类型和 uuid 就是唯一确定一个资源

课后作业

  1. 使用分布式锁完成例子中的内容
  2. 将加锁粒度改为三级头,三级甲,枪和吉利服各自作为一个锁来模拟

广告时间

大家想要评论, 或者讨论技术问题, 找工作内推之类的, 可以进入这个群:

过期了可以微信联系我 : wangwenhan2013 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xMhq3PPb-1602296325267)(/assets/2020053005.png)]
微信:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
假设有一个生产者-消费者模型,多个线程同时访问共享的缓冲区。为了保证线程安全,可以使用互斥锁和条件变量来协调线程之间的操作。 在这个模型中,互斥锁用于保护共享资源,防止多个线程同时访问同一个资源。当一个线程需要访问共享资源时,需要先获取互斥锁,如果互斥锁已经被其他线程占用,那么当前线程将会被阻塞,直到互斥锁被释放。 下面是一个简单的示例代码: ```c++ #include <iostream> #include <pthread.h> #include <queue> using namespace std; queue<int> q; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; void* producer(void* arg) { int count = 0; while (count < 10) { pthread_mutex_lock(&mutex); q.push(count++); cout << "Producer: produced " << count - 1 << endl; pthread_cond_signal(&cond); // 发送信号给消费者线程 pthread_mutex_unlock(&mutex); } return NULL; } void* consumer(void* arg) { while (true) { pthread_mutex_lock(&mutex); if (q.empty()) { pthread_cond_wait(&cond, &mutex); // 等待信号 } int value = q.front(); q.pop(); cout << "Consumer: consumed " << value << endl; pthread_mutex_unlock(&mutex); } return NULL; } int main() { pthread_t tid1, tid2; pthread_create(&tid1, NULL, producer, NULL); pthread_create(&tid2, NULL, consumer, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); return 0; } ``` 在这个示例中,生产者线程不断向队列中添加数据,每次添加完毕后发送一个信号给消费者线程,通知它们可以消费数据了。消费者线程在收到信号后,从队列中取出数据进行处理。如果队列为空,消费者线程将会等待信号,直到有新的数据可以消费。 在这个模型中,互斥锁保护了队列的访问,条件变量用于在生产者和消费者之间传递信号。通过互斥锁和条件变量的协作,可以有效地保证多线程程序的正确性和性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值