Jak używać kolekcji LinkedBlockingQueue?

Kolekcja LinkedBlockingQueue

Kolekcji używamy, gdy chcemy przekazać dużą ilość tekstu lub bardzo duże obiekty (np. obrazy), które nie zmieściłyby się jednorazowo w pamięci albo niepotrzebnie obciążałyby pamięć.

Im większe obiekty tym mniej elementów powinno się jednocześnie znajdować w kolekcji

W moim rozwiązaniu używam czterech klas:

  • Komunikat.java – klasa opisująca obiekt przekazywany
  • Nadawca.java – klasa tworząca i przekazująca komunikaty
  • Printer.java – klasa odbierająca obiekty i w naszym przypadku wypisująca komunikaty na konsoli
  • Main.java – klasa uruchamiająca całość

Kolejka obiektów została ograniczona do 20 komunikatów.

Klasy

Komunikat.java
package blockqueue;
public class Komunikat {
    private String komunikat;
    public void setKomunikat(String text) {
        this.komunikat = text;
    }
    public String getKomunikat() {
        return komunikat;
    }
    @Override
    public String toString() {
        return komunikat;
    }
}
Nadawca.java
package blockqueue;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
public class Nadawca implements Runnable {
    private final LinkedBlockingQueue<Komunikat> lbq;
    private final ArrayList<String> als;
    private int i;
    private boolean done;
    Nadawca(LinkedBlockingQueue<Komunikat> lbq) {
        this.lbq = lbq;
        String str = read(new File("concurrency/src/assets/Winnetou.txt"));
        als = tokenize(str);
        done = false;
    }
    @Override
    public void run() {
        while (!done) {
            String temp = als.get(i);
            if (!temp.equals(null)) {
                Komunikat komunikat = new Komunikat();
                komunikat.setKomunikat(temp);
                try {
                    lbq.put(komunikat);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                i++;
            } else {
                done = true;
            }
        }
    }
    /**
     * Wczytuje plik jako pojedynczy łańcuch znaków
     *
     * @return - plik w postaci stringu
     */
    public static String read(File file) {
        StringBuilder sb = new StringBuilder();
        try {
            try (BufferedReader in = new BufferedReader(new FileReader(file))) {
                String str;
                while ((str = in.readLine()) != null) {
                    sb.append(str);
                }
            }
        } catch (IOException ioe) {
            throw new RuntimeException(ioe);
        }
        return sb.toString();
    }
    /**
     * Tokenizuje string na poszczególne słowa(tokeny) i każde
     * słowo pisuje do arraylisty
     *
     * @param str - string do tokenizacji
     * @return - kolekcje zawierającą ztokenizowany string
     */
    public static ArrayList<String> tokenize(String str) {
        ArrayList<String> al = new ArrayList<>();
        StringTokenizer st = new StringTokenizer(str, " ");
        while (st.hasMoreTokens()) {
            al.add(st.nextToken());
        }
        al.add("###");
        return al;
    }
}
Printer.java
package blockqueue;
import java.util.concurrent.*;
public class Printer implements Runnable {
    private final LinkedBlockingQueue<Komunikat> lbq;
    private Komunikat komunikat = null;
    private boolean notDone;
    Printer(LinkedBlockingQueue<Komunikat> lbq) {
        this.lbq = lbq;
        System.out.println("Drukuję");
        notDone = true;
    }
    @Override
    public void run() {
        while (notDone) {
            try {
                komunikat = lbq.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String temp = komunikat.getKomunikat();
            if (!temp.equals("###")) {
                System.out.println(temp);
            } else {
                System.out.println("Koniec drukowania");
                notDone = false;
                System.exit(0);
            }
        }
    }
}
Main.java
package blockqueue;
import java.util.concurrent.*;
public class Main {
    public static void main(String[] args) {
        LinkedBlockingQueue<Komunikat> lbq = new LinkedBlockingQueue<>(
                20);
        Thread t1 = new Thread(new Nadawca(lbq));
        Thread t2 = new Thread(new Printer(lbq));
        t2.start();
        t1.start();
    }
}

Wynik

Klasa Main uruchamia aplikację. Tworzona jest kolekcja typu LinkedBlockingQueue o pojemności 20 elementów. Następnie tworzone są dwa wątki; wątek Nadawca i wątek Odbiorca, po czym oba wątki rozpoczynają swoją pracę. Oba z nich używają – oczywiście – tej samej kolekcji.

Po uruchomieniu widzimy na konsoli kolejno pobierane i drukowane komunikaty.

Drukuję
,
KAROL
MAY
WINNETOU
TOM
I
ROZDZIAŁ
I
GREENHORN
Czy wiesz,
...
mylę...
KONIEC
TOMU
PIERWSZEGO
Koniec drukowania