Jak odwołać się do jednego obiektu z dwóch wątków najszybciej jak się da?

0

Więc mam coś takiego

ConcurrentHashMap<InetAddress, Connection> connections = new ConcurrentHashMap<>();

Mam też wątek z ServerSocket który nasłuchuje na pakiety. Po adresie pakietu wyszukuję sobie Connection do której pakiet powinien polecieć i zostać przetworzony.

Chciałbym teraz zrobić nie tylko otrzymywanie i przetwarzanie pakietów, ale również odsyłanie informacji z powrotem do klientów. Najlepiej by było gdyby program mógł wysłać np 10-15 pakietów na sekundę w miarę równych odstępach czasu do każdego klienta.

Mógłbym skorzystać z intrinsic lock dla mapy connections ale wtedy przychodzące pakiety mogłyby na chwilę zablokować wątek odpowiedzialny za wysyłanie.

Są jakieś inne sposoby żeby osiągnąć mój cel? Czy ten jest w miarę ok?

1

Chodzi o to że chcesz w sposób "bezpieczny" odczytywać te listę która może być modyfikowana z wielu miejsc ? no to bez synchronizacji się nie obejdzie, musisz się zabezpieczyć nie tylko przed niesynchronicznym dostępem(co w przypadku czytania nie jest szczególnie niebezpieczne) ale przez możliwością czytania danych które są już nieaktualne (stale data)

0

Ok, to zrobimy z synchronizacją. Do tego na Java™ Tutorials znalazłem fajną rzecz LinkedBlockingQueue<T>, użyję jej :D

0

Jedno pytanko mam taki przykładowy kod

byte[] broadcastMessage;

public void zapisuje(byte[] bajty) {
  broadcastMessage = bajty;
}

public byte[] odczytuje() {
  return broadcastMessage;
}

odczytuje będzie wywoływane z kilku wątków, zapisuje z jednego. Chciałbym teraz żeby kilka wątków mogło odczytać coś jednocześnie, ale żeby żaden nie mógł odczytać kiedy się zapisuje.
Oczywistym jest więc żeby lock key obiektu broadcastMessage dostał zapisuje(), bo wtedy żaden wątek nie bd mógł odnieść się do obiektu:

byte[] broadcastMessage;

public void zapisuje(byte[] bajty) {
  synchronized (broadcastMessage) {
    broadcastMessage = bajty;
  }
}

public byte[] odczytuje() {
  return broadcastMessage;
}

Jednak nie jestem pewien czy to będzie dobre rozwiązanie. Nie mogę dać lock key obiektu broadcastMessage przy funkcji odczytuje bo wtedy tylko jeden wątek na raz będzie mógł z niej skorzystać.

Czy osiągnę w ten sposób spodziewany efekt?

2

To co zaklepałeś nie ma dla mnie sensu. Pamiętaj, że synchronized operuje na obiekcie, a nie na referencji. Stąd jeśli referencja się zmieni, to kolejne synchronized będzie działać na kolejnym obiekcie.

Być może twój problem może być rozwiązany przez: http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock

0

Tamto to było takie poglądowe przedstawienie tematu. Mój kod wygląda tak:

    final ArrayList<Element> broadcastMessage = new ArrayList<>();
    
    @Override
    public ArrayList<Element> getBroadcastMessage() 
    {
        return broadcastMessage;
    }

    public void setBroadcastMessage() {
        
        synchronized (broadcastMessage) 
        {
            broadcastMessage.clear();
            for (ConnectionToClient connection : elementList.values()) 
            {
                broadcastMessage.add(connection.getElement());
            }
        }
    }
0

No to masz zły kod, bo może dojść do sytuacji w której jednocześnie odczytujesz i zapisujesz do kolekcji. Wystarczy, że jeden wątek odpali getBM i ją przegląda, a drugi odpali setBM.

Możesz spróbować użyć ReadWriteLock. Ewentualnie, jeżeli np modyfikacje kolekcji są bardzo rzadkie to możesz np użyć niemutowalnej Listy, opakować referencję do Listy w AtomicReference, a w set użyć AtomicReference.compareAndSwap w pętli. Tzn wyglądałoby to tak:

AtomicReference<List<Element>> kolekcja = ???;

get() {
  return kolekcja.get();
}

set() {
  List<Element> snapshot;
  do {
    snapshot = kolekcja.get();
    List<Element> nowa = ???;
    // tutaj wypełniamy listę nowa, ale nie modyfikujemy starej (czyli snapshota)
  } while (!kolekcja.compareAndSet(snapshot, Collections.unmodifiableList(nowa)));
}
0

A gdybym powiedział walić to i niech te odczyty będą po kolei, a nie na raz to wtedy taki kod będzie ok?

final ArrayList<Element> broadcastMessage = new ArrayList<>();
 
    @Override
    public ArrayList<Element> getBroadcastMessage() 
    {
        synchronized (broadcastMessage) {
            return broadcastMessage;
        }
    }
 
    public void setBroadcastMessage() {
 
        synchronized (broadcastMessage) 
        {
            broadcastMessage.clear();
            for (ConnectionToClient connection : elementList.values()) 
            {
                broadcastMessage.add(connection.getElement());
            }
        }
    }
0

@TomRiddle a co jak ktoś dostanie tą referencje i będzie sobie chciał iterować po liście a drugi wątek stuknie clear? ;)

0

@Shalom dopiszę private przed deklaracją :D

A tak serio to nie wiem.

PS: AAaaa, już wiem. O to chodzi?

return broadcastMessage;
return new ArrayList<>(broadcastMessage);
1

A no widzisz ;] Bo jak robisz takie rzeczy to musisz synchronizować każdy dostęp do elementów listy a nie tylko sam fakt otrzymania referencji ;]

edit: No tak, możesz zrobić kopię, to jakieś rozwiązanie, przynajmniej o ile elementy tych kolekcji są immutable.

0

W sumie głupio, bo będzie tyle kopii ile wątków :/ Wiem że każdy komp sobie z tym poradzi i loss of performance nawet nie zauważymy ale nie podoba mi się to i tak.

1 użytkowników online, w tym zalogowanych: 0, gości: 1