多线程程序未按预期运行

问题描述 投票:0回答:1

我试图了解信号量,阅读 Allen B. Downey 的 The Little Book of Semaphores

其中有一个谜题:

概括集合点解决方案。每个线程都应该运行 以下代码:

1 rendezvous
2 critical point

同步要求是在所有线程都执行完集合点之前没有线程执行临界点。 您可以假设有 n 个线程,并且该值存储在一个变量 n 中,所有线程都可以访问该变量。 当前 n − 1 个线程到达时,它们应该阻塞,直到第 n 个线程到达,此时所有线程都可以继续进行

我的解决方案似乎只有时有效,而在其他情况下,它在打印所有集合点和一些但不是全部关键点后会卡住

我无法弄清楚这里出了什么问题

这是我的代码解决方案 -

import java.util.concurrent.*;
import java.util.List;
import java.util.ArrayList;

class Main {
    
    private static final int n = 10;
    private static int counter = 0;
    private static final Semaphore mutex = new Semaphore(1);
    private static final Semaphore barrier = new Semaphore(0);

    private static class Runner implements Runnable {
        
        @Override
        public void run() {
            try {
                System.out.println("Rendezvous");
                mutex.acquire();
                counter = counter + 1;
                mutex.release();
                while (counter < n);
                barrier.release();
                barrier.acquire();
                System.out.println("Critical Section");
                barrier.release();
            } catch (InterruptedException ex) {
                System.out.println("Got exception while executing code for runner : " + ex.getMessage());
            }
        }
    }
    
    public static void main(String[] args) {
        List<Thread> threads = new ArrayList<>();
        for (int i = 0;i < n;i++) {
            Thread t = new Thread(new Runner());
            t.start();
            threads.add(t);
        }
        
        for (Thread t : threads) {
            try {
                t.join();
            } catch (InterruptedException ex) {
                System.out.println("Got exception while executing code for runner : " + ex.getMessage());
            }
        }
        
        
        System.out.println("Completed");
    }
}
java multithreading concurrency semaphore
1个回答
0
投票

代码的问题是:

 while (counter < n);

看起来它正在尝试等待,直到计数器增加到 n。问题是当前线程中没有任何事情可以在循环期间增加计数器。出于优化目的,允许当前线程假设没有其他线程正在操作该变量。例如,编译器可以将值缓存在硬件寄存器中,而无需在每次检查比较时返回到共享内存位置。 还有很多其他事情可能会出错。

如果共享 Integer 不受互斥锁保护(就像这里的情况一样),则可以使用 AtomicInteger 强制将值同步到所有线程,并挫败上述优化,如下所示:

mutex.acquire()
counter.incrementAndGet();
mutex.release()
while( counter.get() < n );

然而,轮询变量的值并不是一个好主意,因为 CPU 大部分时间只是旋转,给宇宙增加热量。有更好的方法来做到这一点。

这里有一个关于如何解决这个难题的非常慷慨的提示:想象一下,有 N 个线程正在等待 acquire() 一个没有许可的信号量。如果您在信号量上释放()N个许可,所有线程都将继续。

© www.soinside.com 2019 - 2024. All rights reserved.