为了账号安全,请及时绑定邮箱和手机立即绑定

与信号量同步的“阶段”序列无法正常工作

与信号量同步的“阶段”序列无法正常工作

开心每一天1111 2023-03-17 15:23:47
我正在学习线程和同步,我正在尝试建立一个“阶段”系统,其中第一阶段生成一条消息(为方便起见,一个 int),然后将其传递给第二阶段,第二阶段修改它(乘以2),然后将其传递到最后一个阶段,该阶段进一步修改它并将其打印到控制台。问题是第三阶段永远不会运行,即使它收到了消息。在我的示例中,我设置了一个名为“资源”的信号量数组,其中信号量 A(索引 0)有 4 个许可,信号量 B 有 3 个,C 有 2 个。所有设置都是公平的。我尝试将信号量设置为公平,但这并没有解决我的问题。我也试过改变睡眠时间,但没有成功。class Fase1 extends Thread{    private int i = 0;    private Semaphore[] resources;    private Fase2 recipient;    public Fase1(Semaphore[] res, Fase2 fase2){        recipient=fase2;        resources=res;    }    @Override    public void run(){        try{            while(true){                resources[0].acquire(2);                resources[1].acquire(2);                recipient.receiveMessage(i);                i++;                sleep(200);                resources[1].release(2);                resources[0].release(2);            }        } catch (InterruptedException e){        }    }}class Fase2 extends Thread{    private Semaphore[] resources;    private Fase3 recipient;    private boolean receivedMessage = false;    private int message = 0;    public Fase2(Semaphore[] res, Fase3 fase3){        recipient=fase3;        resources=res;    }    @Override    public void run(){        try{            while(true){                if(receivedMessage){                    resources[0].acquire(2);                    resources[1].acquire(2);                    resources[2].acquire(2);                    recipient.receiveMessage(message*2);                    receivedMessage = false;                    sleep(200);                    resources[2].release(2);                    resources[1].release(2);                    resources[0].release(2);                }            }        } catch (InterruptedException e){        }    }    public void receiveMessage(int msg){        message = msg;        receivedMessage = true;    }}我注意到许可证不知何故被搞砸了,几乎就像一些线程没有正确释放它们一样,即使这对我来说似乎是正确的。
查看完整描述

1 回答

?
神不在的星期二

TA贡献1963条经验 获得超6个赞

您的设计存在一个根本缺陷:您正在同步对message资源的访问,而不是对receivedMessage标志的访问。当您在线程 #2 中设置标志时true,JVM 没有义务将该写入传播到线程 #3,因为该线程在进入块内之前不会执行同步if,而这很可能永远不会发生。这同样适用于线程#1 和#2 之间的通信。

and操作充当同步点,并且将使写入在线程间可见,因此您需要在检查标志之前调用它们acquire()。例如在:release()Fase3

  @Override

    public void run(){

        try{

            while(true){

                resources[1].acquire(2); // All writes by thread #2 are now visible

                if(receivedMessage){

                    resources[2].acquire(2);

                    System.out.println(message+1);

                    sleep(200);

                    receivedMessage = false;

                    resources[2].release(2);

                }

                resources[1].release(2);

            }

        } catch (InterruptedException e){

        }

    }

还有另一种解决方案,即制作receivedMessageflags volatile,但正确使用单个锁定机制更清晰。

作为旁注,最好使用Runnables 而不是 extendingThread



查看完整回答
反对 回复 2023-03-17
  • 1 回答
  • 0 关注
  • 60 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信