资讯专栏INFORMATION COLUMN

lamport面包店算法简介

zhunjiee / 1768人阅读

摘要:序面包店算法是解决多个线程并发访问一个共享的单用户资源的互斥问题的算法。面包店一次只能接待一位顾客的采购。已知有位顾客要进入面包店采购,按照次序安排他们在前台登记一个签到号码。顾客根据签到号码的由小到大的顺序依次入店购货。

Lamport面包店算法是解决多个线程并发访问一个共享的单用户资源的互斥问题的算法。由莱斯利·兰波特发明。

算法类比

Lamport把这个并发控制算法非常直观地类比为顾客去面包店采购。

面包店一次只能接待一位顾客的采购。

已知有n位顾客要进入面包店采购,按照次序安排他们在前台登记一个签到号码。该签到号码逐次增加1。

顾客根据签到号码的由小到大的顺序依次入店购货。

完成购买的顾客在前台把其签到号码归0。如果完成购买的顾客要再次进店购买,就必须重新排队。

这个类比中的顾客就相当于线程,而入店购货就是进入临界区独占访问该共享资源。由于计算机实现的特点,存在两个线程获得相同的签到号码的情况,这是因为两个线程几乎同时申请排队的签到号码,读取已经发出去的签到号码情况,这两个线程读到的数据是完全一样的,然后各自在读到的数据上找到最大值,再加1作为自己的排队签到号码。

为此,该算法规定如果两个线程的排队签到号码相等,则线程id号较小的具有优先权。

原理

Lamport时间戳原理如下:

每个事件对应一个Lamport时间戳,初始值为0

如果事件在节点内发生,时间戳加1

如果事件属于发送事件,时间戳加1并在消息中带上该时间戳

如果事件属于接收事件,时间戳 = Max(本地时间戳,消息中的时间戳) + 1

5个原则

为了请求资源,进程A发送消息(Tm:A)给所有的其他进程,并且把这个消息放到进程队列中,Tm是消息的时间戳

当进程B接收到了进程A的(Tm:A)请求后,会把它放到自己的请求队列,然后发送一个带时间戳的确认消息给A

为了释放资源,进程A移除所有(Tm:A)的请求消息,然后发送带时间戳的A释放资源请求消息给其他所有的进程

当进程B接收到进程A释放资源的请求,它会移除队列中任意的(Tm:A)的资源请求

当满足以下两个条件时,进程A会被分配该资源:

a)有一个(Tm:A)的请求,按照=>关系排在队列第一位;

b)A接收到了一个时间戳大于Tm的来自所有其他进程的消息

代码示例
private void processRevcMsg(Message m) throws InterruptedException {
        // 原理4 如果事件属于接收事件,时间戳 = Max(本地时间戳,消息中的时间戳) + 1
        clock.update(m.getTimestamp());
        lastSendMap.put(m.getFrom(), m);
        switch (m.getMsgType()) {
            case REQUEST_RES:
                // rule 2 当进程B接收到了进程A的(Tm:A)请求后,会把它放到自己的请求队列,然后发送一个带时间戳的确认消息给A
                addMessageToReqMap(m);
                Message ackMsg = new Message(pid, m.getMsgId(), MessageType.REQUEST_ACK, clock.time());
                // send ack to sender
                sendToTargetProcess(ackMsg,m.getFrom());
                break;
            case REQUEST_ACK:
                break;
            case RELEASE_RES:
                // rule 4 当进程B接收到进程A释放资源的请求,它会移除队列中任意的(Tm:A)的资源请求
                dropMessageFromReqMap(m);
                break;
            default:
                break;
        }
        tryToAcquireResource();
    }

    private void tryToAcquireResource() {
        synchronized (reqMap) {
            if(!reqMap.containsKey(pid) || reqMap.get(pid).isEmpty()){
                return ;
            }

            Message myMessage = reqMap.get(pid).get(0);
            int acceptCount = 1;

            // rule 5 当满足以下两个条件时,进程A会被分配该资源:a)有一个(Tm:A)的请求,按照=>关系排在队列第一位;b)A接收到了一个时间戳大于Tm的来自所有其他进程的消息

            // condition (ii) of rule 5
            // A接收到了一个来自所有其他进程的消息,而且时间戳大于Tm
            for (Map.Entry entry : lastSendMap.entrySet()) {
                if (entry.getKey() == pid) {
                    continue;
                }
                if (isFirstEarlier(myMessage, entry.getValue())) {
                    acceptCount++;
                }else{
                    return ;
                }
            }
            if (!coordinator.hasAcceptedAll(acceptCount)){
                return;
            }

            // condition (i) of rule 5
            // 有一个Tm:A的请求,按照=>关系排在队列第一位
            for (Map.Entry> entry : reqMap.entrySet()) {
                if (entry.getKey() != pid && !entry.getValue().isEmpty()) {
                    if (!isFirstEarlier(myMessage, entry.getValue().get(0))) {
                        return;
                    }
                }
            }

            // remove this request message
            final Message firstMsg = reqMap.get(pid).remove(0);
            workingPool.execute(new Runnable() {
                public void run() {
                    coordinator.acquire(firstMsg.getMsgId(), pid, firstMsg.getTimestamp());
                    // emulate owning resources for a long time
                    try {
                        Thread.sleep(50L);
                        // rule 3 为了释放资源,进程A移除所有(Tm:A)的请求消息,然后发送带时间戳的A释放资源请求消息给其他所有的进程程
                        coordinator.release(firstMsg.getMsgId(), pid, firstMsg.getTimestamp());
                        Message releaseMsg = new Message(pid, firstMsg.getMsgId(),MessageType.RELEASE_RES, clock.time());
                        sendToOtherProcesses(releaseMsg);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            });
        }
    }
doc

Lamport面包店算法

分布式系统理论基础 - 时间、时钟和事件顺序

分布式系统时序基础

lamport

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70377.html

相关文章

  • 什么是拜占庭将军问题

    摘要:在这种状态下,拜占庭将军们才能保证有多于支军队在同一时间一起发起进攻,从而赢取战斗拜占庭将军问题中并不去考虑通信兵是否会被截获或无法传达信息等问题,即消息传递的信道绝无问题。共识算法的核心就是解决拜占庭将军问题分布式网络一致性问题。 本文首发于深入浅出区块链社区原文链接:什么是拜占庭将军问题原文已更新,请读者前往原文阅读 接触区块链的同学,多少都听说过拜占庭将军问题,经常看到或听到某某...

    junnplus 评论0 收藏0
  • Python数据挖掘与机器学习,快速掌握聚类算法和关联分析

    摘要:摘要前文数据挖掘与机器学习技术入门实战与大家分享了分类算法,在本文中将为大家介绍聚类算法和关联分析问题。比如,聚类算法可以实现公司客户价值自动划分,网页自动归类等。 摘要:前文数据挖掘与机器学习技术入门实战与大家分享了分类算法,在本文中将为大家介绍聚类算法和关联分析问题。分类算法与聚类到底有何区别?聚类方法应在怎样的场景下使用?如何使用关联分析算法解决个性化推荐问题?本文就为大家揭晓答...

    Anchorer 评论0 收藏0
  • Paxos共识算法详解

    摘要:只需超过半数的节点达成一致。总结是分布式一致性问题中的重要共识算法。 在一个分布式系统中,由于节点故障、网络延迟等各种原因,根据CAP理论,我们只能保证一致性(Consistency)、可用性(Availability)、分区容错性(Partition Tolerance)中的两个。 对于一致性要求高的系统,比如银行取款机,就会选择牺牲可用性,故障时拒绝服务。MongoDB、Redis...

    Meils 评论0 收藏0
  • 【阅读笔记】——时间复杂度

    摘要:时间复杂度场景一一根长寸的面包,每天吃掉一寸,那么吃完整个面包需要几天答案自然是天可以记作场景二一根寸的面包,每天吃掉剩余的一半,吃的只剩下寸,需要多少天答案以为底,的对数,简写成,所以为天可以记作场景三每天吃掉一个鸡腿,那么吃掉整个鸡腿需 时间复杂度 场景一:一根长10寸的面包,每3天吃掉一寸,那么吃完整个面包需要几天?答案自然是:3×10=30天可以记作:T(n) = 3n 场景二...

    keithxiaoy 评论0 收藏0

发表评论

0条评论

zhunjiee

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<