【问题标题】:Can the Semaphore class enter a deadlock even if the permit is being released?即使正在释放许可证,信号量类也可以进入死锁吗?
【发布时间】:2012-05-17 17:17:02
【问题描述】:

以下并发代码,使用 Java 的 Semaphore 类编写,进入死锁,甚至很难,根据控制台输出,正在释放许可证。

package ThreadTraining;

import java.util.concurrent.Semaphore;

public class ThreadTraining {

    public static class Value {

        private static int value = 0;
        private static final Semaphore SEMAPHORE = new Semaphore(1);

        public static synchronized void acquire() throws InterruptedException {
            SEMAPHORE.acquire();
            System.out.println("A thread has aquired a permit!");
        }

        public static synchronized void release() {
            SEMAPHORE.release();
        }

        public static int get() {
            return value;
        }

        public static void add() {
            value++;
        }

        public static void subtract() {
            value--;
        }
    }

    public static class Adder extends Thread {

        public Adder(String name) {
            this.setName(name);
        }

        @Override
        public void run() {
            System.out.println(this.getName() + " has been created.");
            boolean keepRunning = true;
            while (keepRunning) {
                try {
                    Value.acquire();
                    System.out.print(this.getName() + " has aquired Value's permit. --- ");
                    if (Value.get() > 99) {
                        System.out.print(this.getName() + " has finished it's job. --- ");
                        keepRunning = false;
                    } else {
                        System.out.print(this.getName() + " has modified value from " + Value.get() + "  to ");
                        Value.add();
                        System.out.println(Value.get() + ".");
                    }
                } catch (InterruptedException ie) {
                    System.err.println("This thread was interrupted.");
                } finally {
                    System.out.println(this.getName() + " is releasing Value's permit.");
                    Value.release();
                }
            }
        }
    }

    public static void main(String[] args) {
        Thread threads[] = new Thread[3];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Adder("[Adder]Thread #" + i);
        }
        for (Thread t : threads) {
            t.start();
        }
    }
}

代码的控制台输出:(这是一次“幸运”的运行,它通常只打印到指定的点)

[Adder]Thread #0 has been created.
[Adder]Thread #1 has been created.
[Adder]Thread #2 has been created.
A thread has aquired a permit!
[Adder]Thread #0 has aquired Value's permit. --- [Adder]Thread #0 has modified value from 0  to 1.
[Adder]Thread #0 is releasing Value's permit. /*NOTE: It usually prints only up to this line, hanging after the first permit-release.*/
A thread has aquired a permit!
[Adder]Thread #0 has aquired Value's permit. --- [Adder]Thread #0 has modified value from 1  to 2.
[Adder]Thread #0 is releasing Value's permit.
A thread has aquired a permit!
[Adder]Thread #0 has aquired Value's permit. --- [Adder]Thread #0 has modified value from 2  to 3.
[Adder]Thread #0 is releasing Value's permit.
A thread has aquired a permit!
[Adder]Thread #0 has aquired Value's permit. --- [Adder]Thread #0 has modified value from 3  to 4.
[Adder]Thread #0 is releasing Value's permit.
A thread has aquired a permit!
[Adder]Thread #0 has aquired Value's permit. --- [Adder]Thread #0 has modified value from 4  to 5.
[Adder]Thread #0 is releasing Value's permit.
A thread has aquired a permit!
[Adder]Thread #0 has aquired Value's permit. --- [Adder]Thread #0 has modified value from 5  to 6.
[Adder]Thread #0 is releasing Value's permit.
A thread has aquired a permit!
[Adder]Thread #0 has aquired Value's permit. --- [Adder]Thread #0 has modified value from 6  to 7.
[Adder]Thread #0 is releasing Value's permit.
A thread has aquired a permit!
[Adder]Thread #0 has aquired Value's permit. --- [Adder]Thread #0 has modified value from 7  to 8.
[Adder]Thread #0 is releasing Value's permit.

背后的原因是什么?如果可能,如何解决?


附加信息:

这个问题是my previous concurrency question的“延续”。

新代码非常基于this semaphore tutorial

【问题讨论】:

    标签: java multithreading concurrency deadlock semaphore


    【解决方案1】:

    问题是您自己的方法的同步。 Value.acquireValue.release 都是同步的,因此进入acquire 方法的一个线程将阻止另一个线程调用release,因为release 调用将等待Value 类的监视器被释放,而acquire 内部的那个将等待内部信号量被获取。从您的方法中删除 synchronized -keywords,您将摆脱死锁问题。相反,您可能打算同步您的 get-、add- 和 subtract-方法。

    【讨论】:

    • 嗯,但是从 aquire() 方法中删除 [synchronized] 不会造成多个线程同时调用它的机会吗?我只从 release() 方法中删除了,它似乎已经成功了。
    • 如果多个线程同时调用您的acquire-方法并不重要,因为Semaphore 的获取将只允许获取最大数量的许可(在您的情况下为 1) ,并且当它们用完时,其他线程将阻塞,直到获得所需数量的许可为止(信号量的获取过载,您一次可以获取多个许可)。由于您在信号量中只允许一个许可,因此它的作用类似于互斥锁。详情请见docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/…
    • 我担心 Semaphore 的内部机制可能会出现线程竞争的情况。例如,如果第二个线程(内部)获得了第一个线程已经获得的许可,则在信号量(内部)将许可设置为“不再可获取”之前。我在这个问题之前的最后一个问题提供了这种情况的一个很好的例子(以及它在输出中的后果)。情况可能并不难。
    • @TheLima:如果 java.util.concurrent 中的类本身不是线程安全的,那么我现在会遇到很大的麻烦;)意思是,信号量是为线程而构建的同步。如果 Semaphore 本身具有内部竞争条件,那么 Java 标准库中确实存在严重错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-01
    • 2021-03-20
    相关资源
    最近更新 更多