【问题标题】:Java Concurrency: Exclusive Queue ProblemJava并发:排他队列问题
【发布时间】:2011-05-06 05:06:00
【问题描述】:

我正在尝试为“信号量小书”中的“独占队列”问题编写解决方案。 问题表述如下:

想象线程代表舞厅舞者,两种舞者,领导者和追随者,在进入舞池之前排成两个队列。当领导者到达时,它会检查是否有跟随者在等待。如果是这样,他们都可以继续。否则它会等待。同样,当跟随者到达时,它会检查领导者并相应地继续或等待。此外,每个领导者只能与一个追随者同时调用舞蹈,反之亦然。

书中提到它是使用信号量的解决方案,但我正在尝试使用 Java 中的对象锁来解决它。这是我的解决方案:

ExclusiveQueuePrimitive.java:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ExclusiveQueuePrimitive {

    public static void main(String[] args) throws InterruptedException {
        System.out
                .println("-------------------------------Application START-------------------");
        final int NUM_RUN = 1000;
        // for (int j=0; j<NUM_RUN; j++) {
        for (;;) {
            Counters c = new Counters();
            int NUM_THREADS = 5;

            List<Thread> threads = new ArrayList<Thread>();

            for (int i = 0; i < NUM_THREADS; i++) {
                Thread tl = new Thread(new Leader(c, i + 1));
                Thread tf = new Thread(new Follower(c, i + 1));
                threads.add(tf);
                threads.add(tl);
                tf.start();
                tl.start();
            }
            for (int i = 0; i < threads.size(); i++) {
                Thread t = threads.get(i);
                t.join();
            }
        }
        // System.out.println("--------------------------------Application END-------------------");
    }
}

class Counters {

    public int leaders = 0;
    public int followers = 0;
    //public final Lock countMutex = new ReentrantLock();

    public boolean printed = false;
    public Lock printLock = new ReentrantLock();



    public final Lock leaderQueue = new ReentrantLock();
    public final Lock followerQueue = new ReentrantLock();

    public void dance(String str) {
        System.out.println("" + str);
    }

    public void printLine() {
        System.out.println("");
    }
}

class Leader implements Runnable {

    final Counters c;
    final int num;

    public Leader(Counters counters, int num) {
        this.c = counters;
        this.num = num;
    }

    @Override
    public void run() {

        synchronized (c.leaderQueue) {
            try {
                if (c.followers > 0) {

                        c.followers--;
                        synchronized (c.followerQueue) {
                            c.followerQueue.notify();
                        }


                } else {
                    c.leaders++;

                    c.leaderQueue.wait();
                }
                c.dance("Leader " + num + " called dance");
            } catch (InterruptedException e) {

                e.printStackTrace();
            } 

        }
    }
}

class Follower implements Runnable {

    final Counters c;
    final int num;

    public Follower(Counters counters, int num) {
        this.c = counters;
        this.num = num;
    }

    @Override
    public void run() {

        synchronized (c.followerQueue) {
            try {
                if (c.leaders > 0) {
                    synchronized (c.leaderQueue) {
                        c.leaders--;
                        c.leaderQueue.notify();
                    }
                } else {
                    c.followers++;
                    c.followerQueue.wait();
                }
                c.dance("Follower " + num + " called dance");

            } catch (InterruptedException e) {
                e.printStackTrace();
            } 

        }
    }
}

但是,运行一段时间后,它就挂断了。你能告诉我死锁在哪里以及如何解决它。另外,我想在一对领导者和追随者完成后打印一个新行。我该怎么做?

【问题讨论】:

  • 看起来好像您没有在同一个对象上同步,这不是问题吗?

标签: java concurrency


【解决方案1】:

这是一个典型的僵局:

class Leader {
    synchronized (c.leaderQueue) { ...
        synchronized (c.followerQueue) { ... }
    }
}

class Follower {
    synchronized (c.followerQueue) { ...
        synchronized (c.leaderQueue) { ... }
    }
}

防止这种情况的最简单方法是以相同的顺序获取锁(顺便说一句,同时使用Locksynchronized 不是一个好习惯)。还有其他技术可以检测死锁,但在您的任务上下文中,更改算法应该更有益。

从简单开始——使用单锁使逻辑正确,然后在不破坏正确性的情况下做更多聪明的事情来提高并发性。

【讨论】:

  • @sachin 有几个方法可以解决这个问题:1) 对两个队列使用相同的锁 2) 在 LeaderFollower 中使用相同的锁顺序 3) 摆脱锁定并使用LinkedBlockingQueue 实现相同
【解决方案2】:

c.followerQueue 上有一个互斥体,c.leaderQueue 上有一个互斥体。一方面是先获取leader队列,然后是follower队列,另一方面是先获取follower队列。

这很糟糕。如果一侧抓住从动锁,而另一侧抓住引导锁,则两者都无法继续。您必须避免锁获取的顺序不一致。

要在每对完成后打印一行,只需打印领导者或跟随者,但不能同时打印。领导完成的代码意味着跟随者也完成了......

【讨论】:

  • 如果我使用第三个对象作为锁,我无法弄清楚如何使用,因为同步块将覆盖 if,else 块。但是在 else 中有一个 wait()。
猜你喜欢
  • 2019-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-26
  • 2014-05-09
  • 1970-01-01
  • 2010-12-06
  • 2021-08-14
相关资源
最近更新 更多