Классическая проблема синхронизации Java (хотите попробовать?) - PullRequest
3 голосов
/ 01 апреля 2011

Я пытаюсь решить проблему синхронизации Java. Дело в следующем:

Есть люди класса, и они хотят быть в паре. Итак, у меня есть класс Coupling, который выполняет сопряжение. Когда человек входит в муфту, и ее никто не ждет, она начинает ждать. И ждет, пока кто-нибудь не придет или ей не надоест и не уйдет (таймер предопределения отключается).

Если она заходит и находит кого-то, кто ее ждет, они немедленно соединяются, обмениваются телефонными номерами и идут разными путями. (Продолжите выполнение с информацией другого.)

Один и тот же человек не может уйти с двумя людьми.

Обещаю, что это не университетское упражнение, в котором я пытаюсь обмануть. :) Я просто давно не делал этого, и мне это немного надоело.

Это то, с чем я сначала столкнулся, поэтому Thread пытается установить 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);
}

Ответы [ 4 ]

3 голосов
/ 01 апреля 2011

Если вы действительно хотите разобраться в этом, и это ваш пример проблемы, попробуйте получить копию Java Concurrency in Practice , и убедитесь сами.Это отличная книга.

1 голос
/ 01 апреля 2011

Хорошо, я немного исправлю вашу проблему и предложу решение, затем вы скажете мне, удовлетворены ли вы: -)

Во-первых, вы рассматривали вопрос о том, чтобы сделать операцию "спаривания" асинхронной? Это будет работать примерно так:

  1. Лицо, которое хочет быть в паре, оставляет уведомление в ветке связи;
  2. Когда второй человек оставляет «бесплатное» уведомление, нить Связи уведомляет двух людей.

Как это будет работать в коде? Ну, тебе понадобятся две вещи:

  1. Замок, который поддерживает накопленные разрешения (семафор!);
  2. Потокобезопасная структура данных (ну, на всякий случай. На самом деле не требуется для обеспечения безопасности потоков)

Таким образом, каждый раз, когда Человек хочет соединиться, он уведомляет нить Связывания, которая добавляет Человека в его список «свободных людей» и освобождает одно разрешение от семафора. С другой стороны, соединительная нить постоянно пытается получить разрешения семафора, по два за раз. Поскольку разрешение выдается только тогда, когда Лицо оставляет уведомление, наличие двух разрешений означает, что два человека хотят быть в паре.

Ну, меньше разговоров, больше кода. Вот класс резьбы сцепления:

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);
    }
}

А Человек класс:

public class Person {
    public void notifyPairing(Person other) {
        System.out.printf("I've been paired with %s!\n)", other);
    }
}
1 голос
/ 01 апреля 2011

Как этого не сделать. Грубое решение, которое я собрал после того, как недавно приступил к тестированию чего-то похожего.Имеет проблемы, как отмечено в комментарии ниже.

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).

0 голосов
/ 01 апреля 2011

Простое решение

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...