序列Java带有信号量

发布于 2025-02-13 06:29:20 字数 1184 浏览 0 评论 0 原文

我正在尝试将Aabbaabb的序列归于无限,但我得到的是Aabbaaabb。 Java代码如下:

private static Semaphore semA = new Semaphore(2);
private static Semaphore semB = new Semaphore(0);

public static void main(String[] args) {
    while(true) {
        new P1A().start();
        new P2B().start();
        try {
            Thread.sleep(1000);
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }

}

static class P1A extends Thread{
    
    public void run() {
        try {
            semA.acquire();
            System.out.print("A");
            if(semA.availablePermits() == 0) {
                semB.release(2);
            }
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }
}

static class P2B extends Thread{
    
    public void run() {
        try {
            semB.acquire();
            System.out.print("B");
            if(semB.availablePermits() == 0)
                semA.release(2);
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }
}

根据我的代码,一旦打印了两次,它应该将线程p1a悬挂到线程P2B以进行两次运行。但是,尽管交通灯释放了2个座位,但我无法得到它...

您能帮我吗?

I'm trying to get the AABBAABB sequence to infinity but what I get is AABBAAABB.
The java code is the following:

private static Semaphore semA = new Semaphore(2);
private static Semaphore semB = new Semaphore(0);

public static void main(String[] args) {
    while(true) {
        new P1A().start();
        new P2B().start();
        try {
            Thread.sleep(1000);
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }

}

static class P1A extends Thread{
    
    public void run() {
        try {
            semA.acquire();
            System.out.print("A");
            if(semA.availablePermits() == 0) {
                semB.release(2);
            }
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }
}

static class P2B extends Thread{
    
    public void run() {
        try {
            semB.acquire();
            System.out.print("B");
            if(semB.availablePermits() == 0)
                semA.release(2);
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }
}

According to my code, once printed A twice it should suspend Thread P1A for Thread P2B to run two more times. But I can't get it despite the traffic lights release 2 seats ...

Can you help me?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

情域 2025-02-20 06:29:20

这是您的代码中发生的事情:

  1. 循环开始。线程: a1 b1
  2. a1 打印“ a”。 B1 等待许可证。
  3. 循环在睡觉后再次开始。线程: a2 b2
  4. A2 打印“ A” - 信号量A IS 0中的可用许可证数量,因此将2个许可释放到B信号量。 B2等待许可证。
  5. b1 b2 均获得许可证和打印“ b”。由于两个线程都获得了许可证,因此可用许可证的数量为0-当两个线程检查该条件时,两个发行版2中的2个释放许可证A.信号量a此时有4个许可证,这会导致打印序列瓦解:<代码: aabbaaaabb ...

在将线程标识符添加到打印语句中并在发布后添加其他日志记录允许整个运行看起来像这样( println 使用而不是 print 以获得更好的可读性):

A (Thread-A1)
A (Thread-A2)
B (Thread-B1)
B (Thread-B2)
Thread-A2 released B
Thread-B1 released A
Thread-B2 released A
A (Thread-A3)
A (Thread-A4)
A (Thread-A5)
A (Thread-A6)
Thread-A6 released B
B (Thread-B3)
Thread-B3 released A
B (Thread-B4)
Thread-B4 released A

当然可以多种方法来解决这个问题(我假设不是假设不是最好的,但工作)可能是对目标状态的同步验证(信号量参考必须称为 final ):

if (semA.availablePermits() == 0) {
    synchronized (semB) {
        if (semB.availablePermits() == 0) {
            semB.release(2);
        }
    }
}

由于同步,每个线程到达内部如果有访问权限对于实际(“目标”)状态 - 在这种情况下,不可能进行并行修改。

上面的行为是a compare-and-swap(cas)操作 - 我们验证当前价值是否是我们期望的值,如果是的,我们会采取行动;否则,我们会跳过执行主体的。 Semaphore实际上部分使用此机制( smaphore.sync 内类),但是它会重新恢复增量直到成功(或溢出后抛出):

protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        int current = getState();
        int next = current + releases;
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
        if (compareAndSetState(current, next))
            return true;
    }
}

我创建了 github存储库您可以在上面找到记录结果的代码( initialwithlogging ),带有注释的固定代码( initialfixedwithcomments )和一个重构代码,严格代表了无重复的抽象( SimplifiedSolution )。

Here's what is happening in your code:

  1. Loop starts. Threads: A1, B1 are created.
  2. A1 prints "A". B1 waits for a permit.
  3. Loop starts again after sleeping. Threads: A2, B2 are created.
  4. A2 prints "A" - number of available permits in the semaphore A is 0, so 2 permits are released to the B semaphore. B2 waits for a permit (yet).
  5. B1 and B2 both acquire a permit and print "B". Since both threads acquired the permit, number of available permits is 0 - when both threads check that condition, both release 2 permits in the semaphore A. Semaphore A has 4 permits at this point, which cause the printed sequence to fall apart: AABBAAAABB....

Race between B threads

After adding thread identifiers to the print statements and adding additional logging after releasing permits the whole run looks like this (println used instead of print for better readability):

A (Thread-A1)
A (Thread-A2)
B (Thread-B1)
B (Thread-B2)
Thread-A2 released B
Thread-B1 released A
Thread-B2 released A
A (Thread-A3)
A (Thread-A4)
A (Thread-A5)
A (Thread-A6)
Thread-A6 released B
B (Thread-B3)
Thread-B3 released A
B (Thread-B4)
Thread-B4 released A

There may of course multiple ways of resolving that, one of which (I'd assume not the best, but working) could be a synchronized verification of the target state (Semaphore references have to be declared as final):

if (semA.availablePermits() == 0) {
    synchronized (semB) {
        if (semB.availablePermits() == 0) {
            semB.release(2);
        }
    }
}

Thanks to the synchronization, each thread that reaches the inner if has access to the actual ("target") state - no parallel modification is possible in this case.

The behavior above is a compare-and-swap (CAS) operation - we verify if current value is a value we expect and if so, we act on it; otherwise we skip execution of the if body. Semaphore actually partially uses this mechanism underneath (Semaphore.Sync inner class), but it retries the incrementation until successful (or throws after overflowing):

protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        int current = getState();
        int next = current + releases;
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
        if (compareAndSetState(current, next))
            return true;
    }
}

I've created a GitHub repository where you can find the code for the logging result above (InitialWithLogging), a fixed code with comments (InitialFixedWithComments) and a refactored code that strictly represents the abstraction without duplication (SimplifiedSolution).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文