经典的 Java 同步问题(想尝试一下吗?)

发布于 2024-10-28 06:39:34 字数 936 浏览 7 评论 0原文

我正在尝试解决 Java 同步问题。情况如下:

有类 Person,他们想要配对。所以我有一个耦合类来进行配对。当一个人走进耦合而没有人在等她时,她就开始等待。并等到有人出现或她感到无聊并离开(预定计时器关闭)。

如果她走进去发现有人在等她,他们就会立即联系起来,交换电话号码,然后分道扬镳。 (用对方的信息继续执行。)

同一个人不能带着两个人离开。

我保证这不是我试图作弊的大学练习。:)我只是有一段时间没有做过这些事情了,而且我对此有点生疏。

这是我一开始想到的,所以线程试图设置 Person,如果这不起作用,它将得到 false 作为返回值。然后线程得到服务员。由于显而易见的原因,这将不起作用(调用之间可能会出现另一个线程)以及我将如何向等待线程发出信号以继续进行。

这是我提到的代码:

public class Coupling {
    private static volatile Person waitingPerson = null;

    public static synchronized Integer getWaitingPerson() {
        Integer temp = waitingPerson;
        waitingPerson = null;
        return temp;
    }

    public static synchronized Boolean setWaitingPerson(Integer waitingPerson) {
        if (waitingPerson == null){
            syncro.waitingPerson = waitingPerson;
            return new Boolean(true);
        }
        else
            return new Boolean(false);
}

I'm trying to solve a Java synchronization problem. The case is as follows:

There are class Persons and they want to be paired. So I have a class Coupling that does the pairing. When a person walks in to the Coupling and there is no one waiting for her, she starts to wait. And waits until someone comes along or she gets bored and leaves (predifines timer goes off).

If she walks in and finds someone waiting for her, they are immediately coupled and they exchange phone numbers and go their separate ways. (Continue execution with the other's information.)

The same person can't leave with two people.

I promise that this is not a university exercise I'm trying to cheat in. :) I just havn't done this stuff in a while and I'm a bit rusty at it.

This is something I came with up at first, so the Thread is trying to set the Person, and if this doesn't work it'll get false as return value. Then the thread gets the waiter. For obvious reasons, this will not work (another thread might come between the calls) and how would I signal the waiting thread to go on.

Here's the code I mentioned:

public class Coupling {
    private static volatile Person waitingPerson = null;

    public static synchronized Integer getWaitingPerson() {
        Integer temp = waitingPerson;
        waitingPerson = null;
        return temp;
    }

    public static synchronized Boolean setWaitingPerson(Integer waitingPerson) {
        if (waitingPerson == null){
            syncro.waitingPerson = waitingPerson;
            return new Boolean(true);
        }
        else
            return new Boolean(false);
}

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

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

发布评论

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

评论(4

屋顶上的小猫咪 2024-11-04 06:39:34

如果您真的只是想了解这些内容并且这就是您的示例问题,请尝试获取 Java 并发实践< /a>,并亲自找出答案。这是一本很棒的书。

If you really just want to get into this stuff and this is your example problem, try to get a copy of Java Concurrency in Practice, and find out for yourself. Its a great book.

dawn曙光 2024-11-04 06:39:34

好吧,我会稍微修改一下你的问题并提出一个解决方案,然后你会告诉我你是否满意:-)

首先,你是否考虑过使“配对”操作异步?它的工作原理非常像这样:

  1. 想要配对的人在耦合线程中留下通知;
  2. 当第二个人留下“空闲”通知时,耦合线程会通知这两个人。

这在代码中如何运作?那么,您需要两件事:

  1. 支持累积许可的锁(信号量!);
  2. 一个线程安全的数据结构(好吧,以防万一。它确实不需要是线程安全的)

因此,每次一个人想要配对时,它都会通知耦合线程,该线程添加将人员添加到其“自由人员”列表中,并从信号量中释放一个许可证。另一方面,耦合线程不断尝试获取信号量许可,一次两个。由于只有当一个人留下通知时才会释放许可证,因此拥有两个许可证意味着两个人想要配对。

好吧,少说话,多写代码。这是 Coupling 线程类:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;

public class Coupling implements Runnable {
    private BlockingQueue<Person> peopleQueue;

    private Semaphore semaphore;

    public Coupling() {
        this.peopleQueue = new LinkedBlockingQueue<Person>();
        this.semaphore = new Semaphore(0);
    }

    @Override
    public void run() {
        while(true) {
            // Process incoming "free" events

            try {
                // Acquire permits for two people
                this.semaphore.acquire(2);

                Person pA = this.peopleQueue.poll();
                Person pB = this.peopleQueue.poll();

                pA.notifyPairing(pB);
                pB.notifyPairing(pA);
            } catch (InterruptedException e) {  // This shouldn't really happen..?
                break;
            }
        }
    }

    /**
     * Invoked to notify that a Person is waiting for a coupling
     * @param person
     */
    public void notifyFreePerson(Person person) {
        this.peopleQueue.offer(person);
        this.semaphore.release(1);
    }
}

和 Person 类:

public class Person {
    public void notifyPairing(Person other) {
        System.out.printf("I've been paired with %s!\n)", other);
    }
}

Okay, I'll mangle your problem a little bit and propose a solution, then you'll tell me if you're satisfied :-)

First, have you considered making the "pairing" operation asynchronous? It would work pretty much like this:

  1. A Person who wants to be paired, leaves a notification at a Coupling thread;
  2. When a second person leaves a "free" notification, the Coupling thread notifies the two people.

How would that work in code? Well, you'd need two things:

  1. A lock that supports accumulated permits (a semaphore!);
  2. A thread safe data structure (well, just in case. It really doesn'd need to be thread safe)

So, every time a Person wants to be paired, it notifies the Coupling thread, that adds the Person to its "free people" list and releases one permit from the semaphore. The Coupling thread, on the other hand continuously try to acquire semaphore permits, two at a time. Since a permit is only released when a Person leaves a notification, having two permits means that two people want to be paired.

Well, less talk, more code. Here's the Coupling thread class:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;

public class Coupling implements Runnable {
    private BlockingQueue<Person> peopleQueue;

    private Semaphore semaphore;

    public Coupling() {
        this.peopleQueue = new LinkedBlockingQueue<Person>();
        this.semaphore = new Semaphore(0);
    }

    @Override
    public void run() {
        while(true) {
            // Process incoming "free" events

            try {
                // Acquire permits for two people
                this.semaphore.acquire(2);

                Person pA = this.peopleQueue.poll();
                Person pB = this.peopleQueue.poll();

                pA.notifyPairing(pB);
                pB.notifyPairing(pA);
            } catch (InterruptedException e) {  // This shouldn't really happen..?
                break;
            }
        }
    }

    /**
     * Invoked to notify that a Person is waiting for a coupling
     * @param person
     */
    public void notifyFreePerson(Person person) {
        this.peopleQueue.offer(person);
        this.semaphore.release(1);
    }
}

And the Person class:

public class Person {
    public void notifyPairing(Person other) {
        System.out.printf("I've been paired with %s!\n)", other);
    }
}
与君绝 2024-11-04 06:39:34

如何不这样做。我在最近处理了类似的测试问题后整理出了粗略的解决方案。存在以下评论中指出的问题。

public class Coupling implements Runnable {

    private static Person waitingPerson = null;

    private static Person secondPerson = null;

    private static final Object waitLock = new Object();

    // Time out after a second
    private static final Integer TIMEOUT = 1000;

    public static Person getWaitingPerson(Person incoming) {

        Person match = null;

        // We're the second person in.
        synchronized (waitLock) {
            if (waitingPerson != null) {

                // Get the person who is waiting
                match = waitingPerson;
                waitingPerson = null;
                secondPerson = incoming;

                // Let the other person know
                waitLock.notify();
                return match;
            }
        }

        // We're the first person in, wait for a second
        synchronized(waitLock){
            waitingPerson = incoming;
            try {
                // Wait until someone is available
                waitLock.wait(TIMEOUT);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if(secondPerson != null){
                // Get the other person
                match = secondPerson;
                secondPerson = null;
                return match;
            } else {
                // We timed out, reset
                waitingPerson = null;
                return null;
            }
        }

    }
}

我故意忽略了 Person 类的任何细节(不确定你的 Integer temp 应该做什么)。

How not to do it. Rough solution I put together after tackling something similar for a test recently. Has issues as noted in a comment below.

public class Coupling implements Runnable {

    private static Person waitingPerson = null;

    private static Person secondPerson = null;

    private static final Object waitLock = new Object();

    // Time out after a second
    private static final Integer TIMEOUT = 1000;

    public static Person getWaitingPerson(Person incoming) {

        Person match = null;

        // We're the second person in.
        synchronized (waitLock) {
            if (waitingPerson != null) {

                // Get the person who is waiting
                match = waitingPerson;
                waitingPerson = null;
                secondPerson = incoming;

                // Let the other person know
                waitLock.notify();
                return match;
            }
        }

        // We're the first person in, wait for a second
        synchronized(waitLock){
            waitingPerson = incoming;
            try {
                // Wait until someone is available
                waitLock.wait(TIMEOUT);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if(secondPerson != null){
                // Get the other person
                match = secondPerson;
                secondPerson = null;
                return match;
            } else {
                // We timed out, reset
                waitingPerson = null;
                return null;
            }
        }

    }
}

I've intentionally ignored any specifics of the Person class (not sure what your Integer temp was supposed to do).

用心笑 2024-11-04 06:39:34

一个简单的解决方案

private final Exchanger<Person> waitingPerson = new Exchanger<Person>();

public Person getWaitingPerson(Person person, int timeoutMS) 
        throws InterruptedException, TimeoutException {
    return waitingPerson.exchange(person, timeoutMS, TimeUnit.MILLISECONDS);
}

我假设这些人没有性取向。 ;)

我假设 Person 无法变成 Integer

您谈到了等待,但您不在任何地方等待。 ;)

使用 BlockingQueues 实现起来会简单得多。

顺便说一句:永远不要使用 new Boolean(true)new Boolean(false) 事实上,没有理由不使用 boolean

A simple solution

private final Exchanger<Person> waitingPerson = new Exchanger<Person>();

public Person getWaitingPerson(Person person, int timeoutMS) 
        throws InterruptedException, TimeoutException {
    return waitingPerson.exchange(person, timeoutMS, TimeUnit.MILLISECONDS);
}

I assume that these Persons have no sexual preference. ;)

I assume a Person cannot turn into an Integer

You talked about waiting but you don't wait anywhere. ;)

This would be much simpler to implement using BlockingQueues.

BTW: Never use new Boolean(true) or new Boolean(false) in fact there is no reason not to use boolean

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