sql >> Database >  >> RDS >> Database

Een inleiding tot API's voor gelijktijdige verzamelingen in Java

De gelijktijdige verzameling-API's, afgezien van de Java-verzamelings-API, zijn een reeks verzamelings-API's die speciaal zijn ontworpen en geoptimaliseerd voor gesynchroniseerde multithreaded toegang. Ze zijn gegroepeerd onder de java.util.concurrent pakket. Dit artikel geeft een overzicht en introduceert het gebruik ervan aan de hand van een geschikt voorbeeldscenario.

Een overzicht

Java heeft vanaf het begin multithreading en gelijktijdigheid ondersteund. De threads worden gemaakt door ofwel de Uitvoerbare . te implementeren interface of het uitbreiden van de Thread klas. De synchronisatie wordt bereikt door het trefwoord synchronisatie . Java biedt ook het mechanisme voor communicatie tussen de threads. Dit wordt bereikt met behulp van de notify() en wacht() methoden, die deel uitmaken van het Object klas. Hoewel deze innovatieve multithreading-technieken deel uitmaken van enkele van de uitstekende eigenschappen van Java, schieten ze enigszins tekort in het voorzien in de behoefte van een programmeur die out-of-the-box intensieve multithreading-mogelijkheden nodig heeft. Dit komt omdat een gelijktijdig programma meer nodig heeft dan alleen threads kunnen maken en wat rudimentaire manipulaties kunnen doen. Het vereist veel functies op hoog niveau, zoals threadpools, uitvoeringsmanagers, semaforen, enzovoort.

Bestaand collectiekader

Java heeft al een volwaardig verzamelraamwerk. De collecties zijn erg goed in wat ze doen en kunnen ook worden gebruikt in Java-threadtoepassingen. Er is ook een trefwoord, genaamd gesynchroniseerd , om ze draadveilig te maken. Hoewel het oppervlakkig gezien misschien leuk lijkt om te worden gebruikt in multithreading, is de manier waarop de threadveiligheid wordt bereikt het belangrijkste knelpunt bij de gelijktijdige implementatie ervan. Afgezien van expliciete synchronisatie, zijn ze niet vanaf het begin ontworpen onder het paradigma van gelijktijdige implementatie. Het synchroniseren van deze collecties wordt bereikt door alle toegang tot de status van de collectie te serialiseren. Dit betekent dat, hoewel we enige overeenstemming kunnen hebben, het vanwege de onderliggende geserialiseerde verwerking werkt volgens een principe dat eigenlijk het tegenovergestelde is. Serialisatie weegt zwaar op de prestaties, vooral wanneer meerdere threads strijden om de collectiebrede vergrendeling.

Nieuwe collectie-API's

Gelijktijdige verzameling-API's zijn een aanvulling op Java vanaf versie 5 en maken deel uit van het pakket met de naam java.util.concurrent . Ze zijn een verbetering van bestaande collectie-API's en zijn ontworpen voor gelijktijdige toegang vanuit meerdere threads. Bijvoorbeeld ConcurrentHashMap is eigenlijk de klasse die we nodig hebben als we een gesynchroniseerde op hash gebaseerde Map . willen gebruiken implementatie. Evenzo, als we een traversal-dominante, thread-veilige Lijst . willen , kunnen we de CopyOnWriterArrayList . gebruiken klas. De nieuwe ConcurrentMap interface biedt een aantal samengestelde acties onder één enkele methode, zoals putIfPresent , computeIfPresent , vervangen , samenvoegen , enzovoort. Er zijn veel van dergelijke klassen die binnen het nieuwe kader voor gelijktijdige verzameling vallen. Om er een paar te noemen:ArrayBlockingQueue , ConcurrentLinkedDeque , ConcurrentLinkedQueue , ConcurrentSkipListMap , ConcurrentSkipListSet , CopyOnWriteArraySet , DelayQueue , LinkedBlockingDeque , LinkedBlockingQueue , LinkedTransferQueue , PriorityBlockingQueue , Synchroonwachtrij , en anderen.

Wachtrijen

De verzamelingstypen, zoals Wachtrij en BlockingQueue , kan worden gebruikt om een ​​element tijdelijk vast te houden, in afwachting van het ophalen op een FIFO-manier voor verwerking. ConcurrentLinkQueue , aan de andere kant, is een traditionele FIFO-wachtrij die is geïmplementeerd als een onbegrensde, thread-veilige wachtrij op basis van gekoppelde knooppunten. PriorityBlockingQueue is een onbegrensde blokkeerwachtrij die dezelfde ordeningsnormen gebruikt als die van niet-gelijktijdige PriorityQueue en benodigdheden die ophaaloperaties blokkeren.

Kaarten

In oudere collectieklassen houdt het, wanneer synchronisatie wordt toegepast, vergrendelingen vast voor de duur van elke bewerking. Er zijn bewerkingen, zoals de get methode van HashMap of bevat methode van Lijst , die ingewikkelde berekeningen achter de schermen met zich meebrengen wanneer ze worden aangeroepen. Om bijvoorbeeld een specifiek element in een lijst te vinden, roept het automatisch de equals . op methode. Deze methode vereist een bepaalde berekening om elk element op de lijst te vergelijken; het kan lang duren om de taak te voltooien. Dit is erger in een op hash gebaseerde verzameling. Als de elementen in de hash-kaarten ongelijk verdeeld zijn, kan het erg lang duren om een ​​lange lijst te doorlopen en gelijken aan te roepen. Dit is een probleem omdat het de algehele prestaties van de applicatie kan beïnvloeden.

In tegenstelling tot HashMap , ConcurrentHashMap gebruikt een geheel andere strategie. In plaats van een gemeenschappelijk slot te bieden voor elke gesynchroniseerde methode, gebruikt het een techniek genaamd lock stripping . Dit is een betere oplossing voor zowel gelijktijdigheid als schaalbaarheid. Bij het strippen van sloten worden aparte sloten gebruikt voor aparte emmers. Dientengevolge wordt threadconflict ontkoppeld van de onderliggende gegevensstructuur en in plaats daarvan opgelegd aan de bucket. Bijvoorbeeld, de implementatie van ConcurrentHashMap gebruikt een array van 16 sloten, die elk 1/16 van de hash-buckets bewaken; emmer N wordt bewaakt door sluis N mod 16... dit vermindert de vraag naar een bepaalde sluis met ongeveer een factor 16. Het is dankzij deze techniek dat ConcurrentHashMap ondersteunt standaard ten minste 16 gelijktijdige schrijvers en meer kunnen op aanvraag worden ondergebracht.

CopyOnWriterArrayList

Het is een prima alternatief voor de gesynchroniseerde Lijst en vereist niet dat u een vergrendelingsmechanisme toepast tijdens iteratie. De iterators behouden een verwijzing naar de backing-array aan het begin van de iteratie en veranderen deze niet. Daarom is een korte synchronisatie vereist om de inhoud van de array te krijgen. Meerdere threads hebben toegang tot de collectie zonder elkaar te hinderen. Zelfs wijziging van meerdere threads is niet onderhevig aan strijd. Er is een vaste tegenhanger van deze arraylijst, genaamd CopyOnWriterSet , die kan worden gebruikt om gesynchroniseerde Set . te vervangen op gelijktijdigheid nodig.

Een snel voorbeeld

Er zijn veel klassen in de gelijktijdige verzameling. Het gebruik ervan is niet zo moeilijk voor iedereen die bekend is met het oudere collectiekader. Voor de volledigheid is hier een voorbeeld om een ​​glimp te geven van het gebruik ervan in Java-programmering.

package org.mano.example;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ProducerConsumerDemo {
   static BlockingQueue<Integer> queue = new
      LinkedBlockingQueue<>(5);
   public static void main(String[] args) throws
         InterruptedException {
      int noOfProducers = 7;
      int noOfConsumers = 9;
      for (inti = 0; i < noOfProducers; i++) {
         new Thread(new Producer(), "PRODUCER").start();
      }
      for (int i = 0; i < noOfConsumers; i++) {
         new Thread(new Consumer(), "CONSUMER").start();
      }
      System.exit(0);
   }
   static class Producer implements Runnable {
      Random random = new Random();
      public void run() {
         try {
            int num = random.nextInt(100);
            queue.put(num);
            System.out.println("Produced: " + num
               + " Queue size : "+ queue.size());
            Thread.sleep(100);
         } catch (InterruptedException ex) {
            System.out.println("Producer is interrupted.");
         }
      }
   }
   static class Consumer implements Runnable {
      public void run() {
         try {
            System.out.println("Consumed: " + queue.take()
               + " Queue size : "+ queue.size());
            Thread.sleep(100);
         } catch (InterruptedException ex) {
            System.out.println("Consumer is interrupted.");
         }
      }
   }
}

Conclusie

Misschien wel het grootste voordeel van het gebruik van de gelijktijdige verzamelingsklassen is hun schaalbaarheid en lage risico. De gelijktijdige verzameling-API's van Java bieden een reeks klassen die specifiek zijn ontworpen voor gelijktijdige bewerkingen. Deze klassen zijn alternatieven voor het Java Collection Framework en bieden vergelijkbare functionaliteit, behalve met de aanvullende ondersteuning van gelijktijdigheid. Daarom is de leercurve voor de programmeur die al op de hoogte is van het Java Collection Framework bijna vlak. De klassen worden gedefinieerd in het pakket java.util.concurrent . Hier heb ik geprobeerd een overzicht te geven om aan de slag te gaan en waar nodig de collectie-API's te gebruiken.

Referenties

  • Java API-documentatie
  • Goetz, Brian en Tim Peierls. Java gelijktijdigheid in de praktijk . Pearson, 2013.

  1. MariaDB JSON_CONTAINS() uitgelegd

  2. tempdb Verbeteringen in SQL Server 2019

  3. Wat is SQL Server?

  4. Hoe verander ik de MySQL-tijdzone in een databaseverbinding met Java?