Współbieżność Java - szybki przewodnik
Java to wielowątkowy język programowania, co oznacza, że możemy tworzyć wielowątkowe programy przy użyciu języka Java. Program wielowątkowy zawiera dwie lub więcej części, które mogą działać jednocześnie, a każda część może jednocześnie obsługiwać inne zadanie, optymalnie wykorzystując dostępne zasoby, szczególnie gdy komputer ma wiele procesorów.
Z definicji wielozadaniowość ma miejsce, gdy wiele procesów korzysta ze wspólnych zasobów przetwarzania, takich jak procesor. Wielowątkowość rozszerza ideę wielozadaniowości na aplikacje, w których można podzielić określone operacje w ramach jednej aplikacji na osobne wątki. Każdy z wątków może działać równolegle. System operacyjny dzieli czas przetwarzania nie tylko na różne aplikacje, ale także na każdy wątek w aplikacji.
Wielowątkowość umożliwia pisanie w taki sposób, że wiele działań może być wykonywanych jednocześnie w tym samym programie.
Cykl życia wątku
Wątek przechodzi przez różne etapy swojego cyklu życia. Na przykład wątek rodzi się, uruchamia, biegnie, a następnie umiera. Poniższy diagram przedstawia pełny cykl życia wątku.
Oto etapy cyklu życia -
New- Nowy wątek rozpoczyna swój cykl życia w nowym stanie. Pozostaje w tym stanie, dopóki program nie uruchomi wątku. Jest również określany jako plikborn thread.
Runnable- Po uruchomieniu nowo narodzonego wątku wątek staje się gotowy do uruchomienia. Uważa się, że wątek w tym stanie wykonuje swoje zadanie.
Waiting- Czasami wątek przechodzi w stan oczekiwania, podczas gdy wątek oczekuje na wykonanie zadania przez inny wątek. Wątek przechodzi z powrotem do stanu, który można uruchomić, tylko wtedy, gdy inny wątek sygnalizuje oczekującemu wątkowi kontynuowanie wykonywania.
Timed Waiting- Działający wątek może wejść w czasowy stan oczekiwania przez określony przedział czasu. Wątek w tym stanie przechodzi z powrotem do stanu, który można uruchomić, po wygaśnięciu tego przedziału czasu lub gdy wystąpi zdarzenie, na które oczekuje.
Terminated (Dead) - Wątek, który można uruchomić, przechodzi w stan zakończony po zakończeniu zadania lub w inny sposób.
Priorytety wątków
Każdy wątek Java ma priorytet, który pomaga systemowi operacyjnemu określić kolejność planowania wątków.
Priorytety wątków Java znajdują się w zakresie od MIN_PRIORITY (stała 1) do MAX_PRIORITY (stała 10). Domyślnie każdy wątek ma priorytet NORM_PRIORITY (stała 5).
Wątki o wyższym priorytecie są ważniejsze dla programu i powinny mieć przydzielony czas procesora przed wątkami o niższym priorytecie. Jednak priorytety wątków nie mogą zagwarantować kolejności wykonywania wątków i są bardzo zależne od platformy.
Utwórz wątek, implementując uruchamialny interfejs
Jeśli twoja klasa ma być wykonywana jako wątek, możesz to osiągnąć, implementując plik Runnableberło. Będziesz musiał wykonać trzy podstawowe kroki -
Krok 1
Pierwszym krokiem jest zaimplementowanie metody run () udostępnianej przez plik Runnableberło. Ta metoda zapewnia punkt wejścia dla wątku, a całą logikę biznesową umieścisz w tej metodzie. Poniżej znajduje się prosta składnia metody run () -
public void run( )
Krok 2
W drugim kroku utworzysz instancję Thread obiekt przy użyciu następującego konstruktora -
Thread(Runnable threadObj, String threadName);
Gdzie threadObj jest wystąpieniem klasy, która implementujeRunnable interfejs i threadName to nazwa nadana nowemu wątkowi.
Krok 3
Po utworzeniu obiektu Thread możesz go uruchomić, wywołując start()metoda, która wykonuje wywołanie metody run (). Poniżej znajduje się prosta składnia metody start () -
void start();
Example
Oto przykład, który tworzy nowy wątek i zaczyna go uruchamiać -
class RunnableDemo implements Runnable {
private Thread t;
private String threadName;
RunnableDemo(String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(50);
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo("Thread-1");
R1.start();
RunnableDemo R2 = new RunnableDemo("Thread-2");
R2.start();
}
}
To da następujący wynik -
Output
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Utwórz wątek, rozszerzając klasę wątku
Drugim sposobem utworzenia wątku jest utworzenie nowej klasy, która rozszerza Threadw dwóch prostych krokach. Takie podejście zapewnia większą elastyczność w obsłudze wielu wątków utworzonych przy użyciu metod dostępnych w klasie Thread.
Krok 1
Będziesz musiał zmienić run( )metoda dostępna w klasie Thread. Ta metoda zapewnia punkt wejścia dla wątku, a całą logikę biznesową umieścisz w tej metodzie. Poniżej znajduje się prosta składnia metody run () -
public void run( )
Krok 2
Po utworzeniu obiektu Thread możesz go uruchomić, wywołując start()metoda, która wykonuje wywołanie metody run (). Poniżej znajduje się prosta składnia metody start () -
void start( );
Example
Oto poprzedni program przepisany w celu rozszerzenia wątku -
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
ThreadDemo(String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(50);
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
ThreadDemo T1 = new ThreadDemo("Thread-1");
T1.start();
ThreadDemo T2 = new ThreadDemo("Thread-2");
T2.start();
}
}
To da następujący wynik -
Output
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
W tym rozdziale omówimy różne aspekty tworzenia przyjaznego środowiska dla języka Java.
Konfiguracja środowiska lokalnego
Jeśli nadal chcesz skonfigurować środowisko pod kątem języka programowania Java, w tej sekcji omówiono sposób pobierania i konfigurowania środowiska Java na komputerze. Poniżej przedstawiono kroki konfiguracji środowiska.
Java SE jest dostępna bezpłatnie pod linkiem Pobierz Javę . Możesz pobrać wersję opartą na systemie operacyjnym.
Postępuj zgodnie z instrukcjami, aby pobrać Javę i uruchomić .exeaby zainstalować Javę na swoim komputerze. Po zainstalowaniu Javy na komputerze będziesz musiał ustawić zmienne środowiskowe, aby wskazywały prawidłowe katalogi instalacyjne -
Konfigurowanie ścieżki dla systemu Windows
Zakładając, że zainstalowałeś Javę w katalogu c: \ Program Files \ java \ jdk -
Kliknij prawym przyciskiem myszy „Mój komputer” i wybierz „Właściwości”.
Kliknij przycisk „Zmienne środowiskowe” na karcie „Zaawansowane”.
Teraz zmień zmienną „Path”, tak aby zawierała również ścieżkę do pliku wykonywalnego Java. Na przykład, jeśli ścieżka jest obecnie ustawiona na „C: \ WINDOWS \ SYSTEM32”, zmień ścieżkę na „C: \ WINDOWS \ SYSTEM32; c: \ Program Files \ java \ jdk \ bin”.
Konfigurowanie ścieżki dla systemów Linux, UNIX, Solaris, FreeBSD
Zmienna środowiskowa PATH powinna być ustawiona tak, aby wskazywała, gdzie zostały zainstalowane pliki binarne Java. Zajrzyj do dokumentacji powłoki, jeśli masz z tym problem.
Na przykład, jeśli używasz bash jako powłoki, to dodasz następujący wiersz na końcu swojego '.bashrc: export PATH = / path / to / java: $ PATH'
Popularne edytory Java
Do pisania programów w języku Java potrzebny będzie edytor tekstu. Na rynku dostępne są jeszcze bardziej wyrafinowane środowiska IDE. Ale na razie możesz rozważyć jedną z następujących -
Notepad - Na komputerze z systemem Windows możesz użyć dowolnego prostego edytora tekstu, takiego jak Notatnik (zalecany w tym samouczku), TextPad.
Netbeans - Środowisko Java IDE o otwartym kodzie źródłowym i bezpłatne, które można pobrać z witryny https://netbeans.org/index.html.
Eclipse - Środowisko Java IDE opracowane przez społeczność eclipse open source i można je pobrać z witryny https://www.eclipse.org/.
Core Java zapewnia pełną kontrolę nad programem wielowątkowym. Możesz opracować program wielowątkowy, który można zawiesić, wznowić lub całkowicie zatrzymać w zależności od wymagań. Istnieją różne metody statyczne, których można używać na obiektach wątków, aby kontrolować ich zachowanie. Poniższa tabela zawiera listę tych metod -
Sr.No. | Metoda i opis |
---|---|
1 | public void suspend() Ta metoda wprowadza wątek w stan zawieszenia i można go wznowić za pomocą metody resume (). |
2 | public void stop() Ta metoda całkowicie zatrzymuje wątek. |
3 | public void resume() Ta metoda wznawia wątek, który został zawieszony za pomocą metody suspend (). |
4 | public void wait() Powoduje, że bieżący wątek czeka, aż inny wątek wywoła funkcję notify (). |
5 | public void notify() Budzi pojedynczy wątek, który czeka na monitorze tego obiektu. |
Należy pamiętać, że w najnowszych wersjach języka Java użycie metod suspend (), resume () i stop () jest przestarzałe, dlatego należy skorzystać z dostępnych alternatyw.
Przykład
class RunnableDemo implements Runnable {
public Thread t;
private String threadName;
boolean suspended = false;
RunnableDemo(String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 10; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(300);
synchronized(this) {
while(suspended) {
wait();
}
}
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
void suspend() {
suspended = true;
}
synchronized void resume() {
suspended = false;
notify();
}
}
public class TestThread {
public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo("Thread-1");
R1.start();
RunnableDemo R2 = new RunnableDemo("Thread-2");
R2.start();
try {
Thread.sleep(1000);
R1.suspend();
System.out.println("Suspending First Thread");
Thread.sleep(1000);
R1.resume();
System.out.println("Resuming First Thread");
R2.suspend();
System.out.println("Suspending thread Two");
Thread.sleep(1000);
R2.resume();
System.out.println("Resuming thread Two");
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
} try {
System.out.println("Waiting for threads to finish.");
R1.t.join();
R2.t.join();
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
}
System.out.println("Main thread exiting.");
}
}
Powyższy program generuje następujące dane wyjściowe -
Wynik
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 10
Running Thread-2
Thread: Thread-2, 10
Thread: Thread-1, 9
Thread: Thread-2, 9
Thread: Thread-1, 8
Thread: Thread-2, 8
Thread: Thread-1, 7
Thread: Thread-2, 7
Suspending First Thread
Thread: Thread-2, 6
Thread: Thread-2, 5
Thread: Thread-2, 4
Resuming First Thread
Suspending thread Two
Thread: Thread-1, 6
Thread: Thread-1, 5
Thread: Thread-1, 4
Thread: Thread-1, 3
Resuming thread Two
Thread: Thread-2, 3
Waiting for threads to finish.
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Main thread exiting.
Jeśli jesteś świadomy komunikacji międzyprocesowej, łatwo będzie ci zrozumieć komunikację między wątkami. Komunikacja między wątkami jest ważna, gdy tworzysz aplikację, w której dwa lub więcej wątków wymienia informacje.
Istnieją trzy proste metody i mała sztuczka, która umożliwia komunikację wątkową. Wszystkie trzy metody są wymienione poniżej -
Sr.No. | Metoda i opis |
---|---|
1 | public void wait() Powoduje, że bieżący wątek czeka, aż inny wątek wywoła funkcję notify (). |
2 | public void notify() Budzi pojedynczy wątek, który czeka na monitorze tego obiektu. |
3 | public void notifyAll() Budzi wszystkie wątki, które wywołały wait () na tym samym obiekcie. |
Te metody zostały wdrożone jako finalmetody w Object, więc są dostępne we wszystkich klasach. Wszystkie trzy metody można wywołać tylko z poziomu plikusynchronized kontekst.
Przykład
Ten przykład pokazuje, jak dwa wątki mogą komunikować się przy użyciu wait() i notify()metoda. Możesz stworzyć złożony system, korzystając z tej samej koncepcji.
class Chat {
boolean flag = false;
public synchronized void Question(String msg) {
if (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(msg);
flag = true;
notify();
}
public synchronized void Answer(String msg) {
if (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(msg);
flag = false;
notify();
}
}
class T1 implements Runnable {
Chat m;
String[] s1 = { "Hi", "How are you ?", "I am also doing fine!" };
public T1(Chat m1) {
this.m = m1;
new Thread(this, "Question").start();
}
public void run() {
for (int i = 0; i < s1.length; i++) {
m.Question(s1[i]);
}
}
}
class T2 implements Runnable {
Chat m;
String[] s2 = { "Hi", "I am good, what about you?", "Great!" };
public T2(Chat m2) {
this.m = m2;
new Thread(this, "Answer").start();
}
public void run() {
for (int i = 0; i < s2.length; i++) {
m.Answer(s2[i]);
}
}
}
public class TestThread {
public static void main(String[] args) {
Chat m = new Chat();
new T1(m);
new T2(m);
}
}
Gdy powyższy program jest przestrzegany i wykonywany, daje następujący wynik -
Wynik
Hi
Hi
How are you ?
I am good, what about you?
I am also doing fine!
Great!
Powyższy przykład został pobrany, a następnie zmodyfikowany z [https://stackoverflow.com/questions/2170520/inter-thread-communication-in-java]
Przykład wielowątkowości z synchronizacją
Oto ten sam przykład, który wyświetla sekwencję wartości licznika i za każdym razem, gdy go uruchamiamy, daje ten sam wynik.
Przykład
class PrintDemo {
public void printCount() {
try {
for(int i = 5; i > 0; i--) {
System.out.println("Counter --- " + i );
}
} catch (Exception e) {
System.out.println("Thread interrupted.");
}
}
}
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
PrintDemo PD;
ThreadDemo(String name, PrintDemo pd) {
threadName = name;
PD = pd;
}
public void run() {
synchronized(PD) {
PD.printCount();
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
PrintDemo PD = new PrintDemo();
ThreadDemo T1 = new ThreadDemo("Thread - 1 ", PD);
ThreadDemo T2 = new ThreadDemo("Thread - 2 ", PD);
T1.start();
T2.start();
// wait for threads to end
try {
T1.join();
T2.join();
} catch (Exception e) {
System.out.println("Interrupted");
}
}
}
Daje to ten sam wynik za każdym razem, gdy uruchamiasz ten program -
Wynik
Starting Thread - 1
Starting Thread - 2
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread - 1 exiting.
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread - 2 exiting.
Zakleszczenie opisuje sytuację, w której dwa lub więcej wątków jest zablokowanych na zawsze, czekając na siebie. Zakleszczenie występuje, gdy wiele wątków wymaga tych samych blokad, ale uzyskuje je w innej kolejności. Program wielowątkowy Java może cierpieć z powodu zakleszczenia, ponieważsynchronizedsłowo kluczowe powoduje, że wątek wykonawczy blokuje się podczas oczekiwania na blokadę lub monitor skojarzony z określonym obiektem. Oto przykład.
Przykład
public class TestThread {
public static Object Lock1 = new Object();
public static Object Lock2 = new Object();
public static void main(String args[]) {
ThreadDemo1 T1 = new ThreadDemo1();
ThreadDemo2 T2 = new ThreadDemo2();
T1.start();
T2.start();
}
private static class ThreadDemo1 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (Lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}
private static class ThreadDemo2 extends Thread {
public void run() {
synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 2...");
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 1...");
synchronized (Lock1) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
}
Kiedy kompilujesz i wykonujesz powyższy program, znajdujesz sytuację zakleszczenia, a poniżej znajdują się dane wyjściowe programu -
Wynik
Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...
Powyższy program zawiesi się na zawsze, ponieważ żaden z wątków nie jest w stanie kontynuować i nie czeka na siebie nawzajem, aby zwolnić blokadę, więc możesz wyjść z programu naciskając CTRL + C.
Przykład rozwiązania impasu
Zmieńmy kolejność blokady i uruchommy ten sam program, aby zobaczyć, czy oba wątki nadal na siebie czekają -
Przykład
public class TestThread {
public static Object Lock1 = new Object();
public static Object Lock2 = new Object();
public static void main(String args[]) {
ThreadDemo1 T1 = new ThreadDemo1();
ThreadDemo2 T2 = new ThreadDemo2();
T1.start();
T2.start();
}
private static class ThreadDemo1 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (Lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}
private static class ThreadDemo2 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 2: Holding lock 1...");
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 2...");
synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
}
Więc po prostu zmiana kolejności blokad zapobiega wejściu programu w sytuację zakleszczenia i kończy się następującym wynikiem -
Wynik
Thread 1: Holding lock 1...
Thread 1: Waiting for lock 2...
Thread 1: Holding lock 1 & 2...
Thread 2: Holding lock 1...
Thread 2: Waiting for lock 2...
Thread 2: Holding lock 1 & 2...
Powyższy przykład ma na celu po prostu wyjaśnienie koncepcji, jednak jest to koncepcja złożona i należy się do niej zagłębić, zanim opracujesz aplikacje, które radzą sobie z sytuacjami zakleszczenia.
Klasa ThreadLocal służy do tworzenia zmiennych lokalnych wątku, które mogą być odczytywane i zapisywane tylko przez ten sam wątek. Na przykład, jeśli dwa wątki uzyskują dostęp do kodu mającego odwołanie do tej samej zmiennej threadLocal, to każdy wątek nie zobaczy żadnej modyfikacji zmiennej threadLocal wykonanej przez inny wątek.
ThreadLocal Methods
Poniżej znajduje się lista ważnych metod dostępnych w klasie ThreadLocal.
Sr.No. | Metoda i opis |
---|---|
1 | public T get() Zwraca wartość w kopii tej zmiennej lokalnej wątku w bieżącym wątku. |
2 | protected T initialValue() Zwraca „wartość początkową” bieżącego wątku dla tej zmiennej lokalnej wątku. |
3 | public void remove() Usuwa bieżącą wartość wątku dla tej zmiennej lokalnej wątku. |
4 | public void set(T value) Ustawia kopię bieżącego wątku tej zmiennej lokalnej wątku na określoną wartość. |
Przykład
Poniższy program TestThread demonstruje niektóre z tych metod klasy ThreadLocal. Tutaj użyliśmy dwóch zmiennych licznikowych, jedna to normalna zmienna, a druga to ThreadLocal.
class RunnableDemo implements Runnable {
int counter;
ThreadLocal<Integer> threadLocalCounter = new ThreadLocal<Integer>();
public void run() {
counter++;
if(threadLocalCounter.get() != null) {
threadLocalCounter.set(threadLocalCounter.get().intValue() + 1);
} else {
threadLocalCounter.set(0);
}
System.out.println("Counter: " + counter);
System.out.println("threadLocalCounter: " + threadLocalCounter.get());
}
}
public class TestThread {
public static void main(String args[]) {
RunnableDemo commonInstance = new RunnableDemo();
Thread t1 = new Thread(commonInstance);
Thread t2 = new Thread(commonInstance);
Thread t3 = new Thread(commonInstance);
Thread t4 = new Thread(commonInstance);
t1.start();
t2.start();
t3.start();
t4.start();
// wait for threads to end
try {
t1.join();
t2.join();
t3.join();
t4.join();
} catch (Exception e) {
System.out.println("Interrupted");
}
}
}
Spowoduje to następujący wynik.
Wynik
Counter: 1
threadLocalCounter: 0
Counter: 2
threadLocalCounter: 0
Counter: 3
threadLocalCounter: 0
Counter: 4
threadLocalCounter: 0
Możesz zobaczyć, że wartość counter jest zwiększana przez każdy wątek, ale threadLocalCounter pozostaje 0 dla każdego wątku.
Klasa java.util.concurrent.ThreadLocalRandom jest klasą narzędziową wprowadzoną od jdk 1.7 i nowszą i jest przydatna, gdy do generowania liczb losowych wymagane jest wiele wątków lub ForkJoinTasks. Poprawia wydajność i ma mniej rywalizacji niż metoda Math.random ().
ThreadLocalRandom Methods
Poniżej znajduje się lista ważnych metod dostępnych w klasie ThreadLocalRandom.
Sr.No. | Metoda i opis |
---|---|
1 | public static ThreadLocalRandom current() Zwraca ThreadLocalRandom bieżącego wątku. |
2 | protected int next(int bits) Generuje następną liczbę pseudolosową. |
3 | public double nextDouble(double n) Zwraca pseudolosową, równomiernie rozłożoną podwójną wartość między 0 (włącznie) a określoną wartością (wyłączność). |
4 | public double nextDouble(double least, double bound) Zwraca pseudolosową, równomiernie rozłożoną wartość między podaną najmniejszą wartością (włącznie) i powiązaną (wyłączną). |
5 | public int nextInt(int least, int bound) Zwraca pseudolosową, równomiernie rozłożoną wartość między podaną najmniejszą wartością (włącznie) i powiązaną (wyłączną). |
6 | public long nextLong(long n) Zwraca pseudolosową, równomiernie rozłożoną wartość między 0 (włącznie) a określoną wartością (wyłączność). |
7 | public long nextLong(long least, long bound) Zwraca pseudolosową, równomiernie rozłożoną wartość między podaną najmniejszą wartością (włącznie) i powiązaną (wyłączną). |
8 | public void setSeed(long seed) Zgłasza UnsupportedOperationException. |
Przykład
Poniższy program TestThread demonstruje niektóre z tych metod interfejsu Lock. Tutaj użyliśmy lock (), aby uzyskać blokadę i odblokować (), aby zwolnić blokadę.
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ThreadLocalRandom;
public class TestThread {
public static void main(final String[] arguments) {
System.out.println("Random Integer: " + new Random().nextInt());
System.out.println("Seeded Random Integer: " + new Random(15).nextInt());
System.out.println(
"Thread Local Random Integer: " + ThreadLocalRandom.current().nextInt());
final ThreadLocalRandom random = ThreadLocalRandom.current();
random.setSeed(15); //exception will come as seeding is not allowed in ThreadLocalRandom.
System.out.println("Seeded Thread Local Random Integer: " + random.nextInt());
}
}
Spowoduje to następujący wynik.
Wynik
Random Integer: 1566889198
Seeded Random Integer: -1159716814
Thread Local Random Integer: 358693993
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.concurrent.ThreadLocalRandom.setSeed(Unknown Source)
at TestThread.main(TestThread.java:21)
Tutaj użyliśmy klas ThreadLocalRandom i Random, aby uzyskać liczby losowe.
Interfejs java.util.concurrent.locks.Lock jest używany jako mechanizm synchronizacji wątków podobny do synchronizowanych bloków. Nowy mechanizm blokujący jest bardziej elastyczny i zapewnia więcej opcji niż blok synchroniczny. Główne różnice między zamkiem a blokiem zsynchronizowanym są następujące -
Guarantee of sequence- Zsynchronizowany blok nie zapewnia żadnej gwarancji kolejności, w jakiej oczekujący wątek uzyska dostęp. Interfejs blokady obsługuje to.
No timeout- Zsynchronizowany blok nie ma opcji limitu czasu, jeśli blokada nie jest przyznana. Interfejs blokady zapewnia taką opcję.
Single method - Zsynchronizowany blok musi być w pełni zawarty w jednej metodzie, podczas gdy metody lock () i unlock () interfejsu blokady mogą być wywoływane różnymi metodami.
Metody blokowania
Poniżej znajduje się lista ważnych metod dostępnych w klasie Lock.
Sr.No. | Metoda i opis |
---|---|
1 | public void lock() Przejmuje zamek. |
2 | public void lockInterruptibly() Uzyskuje blokadę, chyba że bieżący wątek zostanie przerwany. |
3 | public Condition newCondition() Zwraca nowe wystąpienie Condition, które jest powiązane z tym wystąpieniem Lock. |
4 | public boolean tryLock() Uzyskuje blokadę tylko wtedy, gdy jest wolna w momencie wywołania. |
5 | public boolean tryLock() Uzyskuje blokadę tylko wtedy, gdy jest wolna w momencie wywołania. |
6 | public boolean tryLock(long time, TimeUnit unit) Uzyskuje blokadę, jeśli jest wolna w podanym czasie oczekiwania, a bieżący wątek nie został przerwany. |
7 | public void unlock() Zwalnia blokadę. |
Przykład
Poniższy program TestThread demonstruje niektóre z tych metod interfejsu Lock. Tutaj użyliśmy lock (), aby uzyskać blokadę i odblokować (), aby zwolnić blokadę.
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class PrintDemo {
private final Lock queueLock = new ReentrantLock();
public void print() {
queueLock.lock();
try {
Long duration = (long) (Math.random() * 10000);
System.out.println(Thread.currentThread().getName()
+ " Time Taken " + (duration / 1000) + " seconds.");
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.printf(
"%s printed the document successfully.\n", Thread.currentThread().getName());
queueLock.unlock();
}
}
}
class ThreadDemo extends Thread {
PrintDemo printDemo;
ThreadDemo(String name, PrintDemo printDemo) {
super(name);
this.printDemo = printDemo;
}
@Override
public void run() {
System.out.printf(
"%s starts printing a document\n", Thread.currentThread().getName());
printDemo.print();
}
}
public class TestThread {
public static void main(String args[]) {
PrintDemo PD = new PrintDemo();
ThreadDemo t1 = new ThreadDemo("Thread - 1 ", PD);
ThreadDemo t2 = new ThreadDemo("Thread - 2 ", PD);
ThreadDemo t3 = new ThreadDemo("Thread - 3 ", PD);
ThreadDemo t4 = new ThreadDemo("Thread - 4 ", PD);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
Spowoduje to następujący wynik.
Wynik
Thread - 1 starts printing a document
Thread - 4 starts printing a document
Thread - 3 starts printing a document
Thread - 2 starts printing a document
Thread - 1 Time Taken 4 seconds.
Thread - 1 printed the document successfully.
Thread - 4 Time Taken 3 seconds.
Thread - 4 printed the document successfully.
Thread - 3 Time Taken 5 seconds.
Thread - 3 printed the document successfully.
Thread - 2 Time Taken 4 seconds.
Thread - 2 printed the document successfully.
Użyliśmy tutaj klasy ReentrantLock jako implementacji interfejsu Lock. Klasa ReentrantLock umożliwia wątkowi blokowanie metody, nawet jeśli ma już blokadę innej metody.
Interfejs java.util.concurrent.locks.ReadWriteLock umożliwia jednoczesne odczytywanie wielu wątków, ale tylko jeden wątek może pisać jednocześnie.
Read Lock - Jeśli żaden wątek nie zablokował ReadWriteLock do zapisu, wiele wątków może uzyskać dostęp do blokady odczytu.
Write Lock - Jeśli żaden wątek nie odczytuje ani nie pisze, jeden wątek może uzyskać dostęp do blokady zapisu.
Metody blokowania
Poniżej znajduje się lista ważnych metod dostępnych w klasie Lock.
Sr.No. | Metoda i opis |
---|---|
1 | public Lock readLock() Zwraca blokadę używaną do odczytu. |
2 | public Lock writeLock() Zwraca blokadę używaną do pisania. |
Przykład
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class TestThread {
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
private static String message = "a";
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new WriterA());
t1.setName("Writer A");
Thread t2 = new Thread(new WriterB());
t2.setName("Writer B");
Thread t3 = new Thread(new Reader());
t3.setName("Reader");
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
}
static class Reader implements Runnable {
public void run() {
if(lock.isWriteLocked()) {
System.out.println("Write Lock Present.");
}
lock.readLock().lock();
try {
Long duration = (long) (Math.random() * 10000);
System.out.println(Thread.currentThread().getName()
+ " Time Taken " + (duration / 1000) + " seconds.");
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() +": "+ message );
lock.readLock().unlock();
}
}
}
static class WriterA implements Runnable {
public void run() {
lock.writeLock().lock();
try {
Long duration = (long) (Math.random() * 10000);
System.out.println(Thread.currentThread().getName()
+ " Time Taken " + (duration / 1000) + " seconds.");
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
message = message.concat("a");
lock.writeLock().unlock();
}
}
}
static class WriterB implements Runnable {
public void run() {
lock.writeLock().lock();
try {
Long duration = (long) (Math.random() * 10000);
System.out.println(Thread.currentThread().getName()
+ " Time Taken " + (duration / 1000) + " seconds.");
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
message = message.concat("b");
lock.writeLock().unlock();
}
}
}
}
Spowoduje to następujący wynik.
Wynik
Writer A Time Taken 6 seconds.
Write Lock Present.
Writer B Time Taken 2 seconds.
Reader Time Taken 0 seconds.
Reader: aab
Interfejs java.util.concurrent.locks.Condition zapewnia wątkowi możliwość wstrzymania wykonania, dopóki podany warunek nie zostanie spełniony. Obiekt Condition jest koniecznie powiązany z Lock i należy go uzyskać za pomocą metody newCondition ().
Metody warunkowania
Poniżej znajduje się lista ważnych metod dostępnych w klasie Condition.
Sr.No. | Metoda i opis |
---|---|
1 | public void await() Powoduje, że bieżący wątek czeka, aż zostanie zasygnalizowany lub przerwany. |
2 | public boolean await(long time, TimeUnit unit) Powoduje, że bieżący wątek czeka, aż zostanie zasygnalizowany lub przerwany albo upłynie określony czas oczekiwania. |
3 | public long awaitNanos(long nanosTimeout) Powoduje, że bieżący wątek czeka, aż zostanie zasygnalizowany lub przerwany albo upłynie określony czas oczekiwania. |
4 | public long awaitUninterruptibly() Powoduje, że bieżący wątek czeka, aż zostanie zasygnalizowany. |
5 | public long awaitUntil() Powoduje, że bieżący wątek czeka, aż zostanie zasygnalizowany lub przerwany albo upłynie określony termin. |
6 | public void signal() Budzi jeden oczekujący wątek. |
7 | public void signalAll() Budzi wszystkie oczekujące wątki. |
Przykład
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestThread {
public static void main(String[] args) throws InterruptedException {
ItemQueue itemQueue = new ItemQueue(10);
//Create a producer and a consumer.
Thread producer = new Producer(itemQueue);
Thread consumer = new Consumer(itemQueue);
//Start both threads.
producer.start();
consumer.start();
//Wait for both threads to terminate.
producer.join();
consumer.join();
}
static class ItemQueue {
private Object[] items = null;
private int current = 0;
private int placeIndex = 0;
private int removeIndex = 0;
private final Lock lock;
private final Condition isEmpty;
private final Condition isFull;
public ItemQueue(int capacity) {
this.items = new Object[capacity];
lock = new ReentrantLock();
isEmpty = lock.newCondition();
isFull = lock.newCondition();
}
public void add(Object item) throws InterruptedException {
lock.lock();
while(current >= items.length)
isFull.await();
items[placeIndex] = item;
placeIndex = (placeIndex + 1) % items.length;
++current;
//Notify the consumer that there is data available.
isEmpty.signal();
lock.unlock();
}
public Object remove() throws InterruptedException {
Object item = null;
lock.lock();
while(current <= 0) {
isEmpty.await();
}
item = items[removeIndex];
removeIndex = (removeIndex + 1) % items.length;
--current;
//Notify the producer that there is space available.
isFull.signal();
lock.unlock();
return item;
}
public boolean isEmpty() {
return (items.length == 0);
}
}
static class Producer extends Thread {
private final ItemQueue queue;
public Producer(ItemQueue queue) {
this.queue = queue;
}
@Override
public void run() {
String[] numbers =
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"};
try {
for(String number: numbers) {
System.out.println("[Producer]: " + number);
}
queue.add(null);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
static class Consumer extends Thread {
private final ItemQueue queue;
public Consumer(ItemQueue queue) {
this.queue = queue;
}
@Override
public void run() {
try {
do {
Object number = queue.remove();
System.out.println("[Consumer]: " + number);
if(number == null) {
return;
}
} while(!queue.isEmpty());
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
Spowoduje to następujący wynik.
Wynik
[Producer]: 1
[Producer]: 2
[Producer]: 3
[Producer]: 4
[Producer]: 5
[Producer]: 6
[Producer]: 7
[Producer]: 8
[Producer]: 9
[Producer]: 10
[Producer]: 11
[Producer]: 12
[Consumer]: null
Klasa java.util.concurrent.atomic.AtomicInteger udostępnia operacje na bazowej wartości int, które mogą być odczytywane i zapisywane atomowo, a także zawiera zaawansowane operacje atomowe. AtomicInteger obsługuje niepodzielne operacje na bazowej zmiennej int. Posiada metody pobierania i ustawiania, które działają jak odczyty i zapisy na zmiennych zmiennych. Oznacza to, że zestaw ma relację zdarzenie przed każdym kolejnym uzyskaniem tej samej zmiennej. Atomowa metoda compareAndSet również ma te funkcje spójności pamięci.
Metody AtomicInteger
Poniżej znajduje się lista ważnych metod dostępnych w klasie AtomicInteger.
Sr.No. | Metoda i opis |
---|---|
1 | public int addAndGet(int delta) Atomowo dodaje podaną wartość do bieżącej wartości. |
2 | public boolean compareAndSet(int expect, int update) Atomowo ustawia wartość na podaną zaktualizowaną wartość, jeśli bieżąca wartość jest taka sama jak wartość oczekiwana. |
3 | public int decrementAndGet() Zmniejsza atomowo bieżącą wartość o jeden. |
4 | public double doubleValue() Zwraca wartość określonej liczby jako podwójną. |
5 | public float floatValue() Zwraca wartość określonej liczby jako liczbę zmiennoprzecinkową. |
6 | public int get() Pobiera bieżącą wartość. |
7 | public int getAndAdd(int delta) Atomiclly dodaje podaną wartość do bieżącej wartości. |
8 | public int getAndDecrement() Zmniejsza atomowo bieżącą wartość o jeden. |
9 | public int getAndIncrement() Zwiększa atomowo bieżącą wartość o jeden. |
10 | public int getAndSet(int newValue) Atomowo ustawia podaną wartość i zwraca starą wartość. |
11 | public int incrementAndGet() Zwiększa atomowo bieżącą wartość o jeden. |
12 | public int intValue() Zwraca wartość określonej liczby jako liczbę całkowitą. |
13 | public void lazySet(int newValue) Ostatecznie ustawia się na podaną wartość. |
14 | public long longValue() Zwraca wartość określonej liczby w postaci długości. |
15 | public void set(int newValue) Ustawia podaną wartość. |
16 | public String toString() Zwraca ciąg znaków reprezentujący bieżącą wartość. |
17 | public boolean weakCompareAndSet(int expect, int update) Atomowo ustawia wartość na podaną zaktualizowaną wartość, jeśli bieżąca wartość jest taka sama jak wartość oczekiwana. |
Przykład
Poniższy program TestThread przedstawia niebezpieczną implementację licznika w środowisku opartym na wątkach.
public class TestThread {
static class Counter {
private int c = 0;
public void increment() {
c++;
}
public int value() {
return c;
}
}
public static void main(final String[] arguments) throws InterruptedException {
final Counter counter = new Counter();
//1000 threads
for(int i = 0; i < 1000 ; i++) {
new Thread(new Runnable() {
public void run() {
counter.increment();
}
}).start();
}
Thread.sleep(6000);
System.out.println("Final number (should be 1000): " + counter.value());
}
}
Może to dać następujący wynik w zależności od szybkości komputera i przeplatania wątków.
Wynik
Final number (should be 1000): 1000
Przykład
import java.util.concurrent.atomic.AtomicInteger;
public class TestThread {
static class Counter {
private AtomicInteger c = new AtomicInteger(0);
public void increment() {
c.getAndIncrement();
}
public int value() {
return c.get();
}
}
public static void main(final String[] arguments) throws InterruptedException {
final Counter counter = new Counter();
//1000 threads
for(int i = 0; i < 1000 ; i++) {
new Thread(new Runnable() {
public void run() {
counter.increment();
}
}).start();
}
Thread.sleep(6000);
System.out.println("Final number (should be 1000): " + counter.value());
}
}
Spowoduje to następujący wynik.
Wynik
Final number (should be 1000): 1000
Klasa java.util.concurrent.atomic.AtomicLong udostępnia operacje na bazowych długich wartościach, które mogą być odczytywane i zapisywane atomowo, a także zawiera zaawansowane operacje atomowe. AtomicLong obsługuje niepodzielne operacje na bazowej zmiennej długiej. Posiada metody pobierania i ustawiania, które działają jak odczyty i zapisy na zmiennych zmiennych. Oznacza to, że zestaw ma relację zdarzenie przed każdym kolejnym uzyskaniem tej samej zmiennej. Atomowa metoda compareAndSet również ma te funkcje spójności pamięci.
Metody AtomicLong
Poniżej znajduje się lista ważnych metod dostępnych w klasie AtomicLong.
Sr.No. | Metoda i opis |
---|---|
1 | public long addAndGet(long delta) Atomowo dodaje podaną wartość do bieżącej wartości. |
2 | public boolean compareAndSet(long expect, long update) Atomowo ustawia wartość na podaną zaktualizowaną wartość, jeśli bieżąca wartość jest taka sama jak wartość oczekiwana. |
3 | public long decrementAndGet() Zmniejsza atomowo bieżącą wartość o jeden. |
4 | public double doubleValue() Zwraca wartość określonej liczby jako podwójną. |
5 | public float floatValue() Zwraca wartość określonej liczby jako liczbę zmiennoprzecinkową. |
6 | public long get() Pobiera bieżącą wartość. |
7 | public long getAndAdd(long delta) Atomiclly dodaje podaną wartość do bieżącej wartości. |
8 | public long getAndDecrement() Zmniejsza atomowo bieżącą wartość o jeden. |
9 | public long getAndIncrement() Zwiększa atomowo bieżącą wartość o jeden. |
10 | public long getAndSet(long newValue) Atomowo ustawia podaną wartość i zwraca starą wartość. |
11 | public long incrementAndGet() Zwiększa atomowo bieżącą wartość o jeden. |
12 | public int intValue() Zwraca wartość określonej liczby jako liczbę całkowitą. |
13 | public void lazySet(long newValue) Ostatecznie ustawia się na podaną wartość. |
14 | public long longValue() Zwraca wartość określonej liczby w postaci długości. |
15 | public void set(long newValue) Ustawia podaną wartość. |
16 | public String toString() Zwraca ciąg znaków reprezentujący bieżącą wartość. |
17 | public boolean weakCompareAndSet(long expect, long update) Atomowo ustawia wartość na podaną zaktualizowaną wartość, jeśli bieżąca wartość jest taka sama jak wartość oczekiwana. |
Przykład
Poniższy program TestThread przedstawia bezpieczną implementację licznika przy użyciu AtomicLong w środowisku opartym na wątkach.
import java.util.concurrent.atomic.AtomicLong;
public class TestThread {
static class Counter {
private AtomicLong c = new AtomicLong(0);
public void increment() {
c.getAndIncrement();
}
public long value() {
return c.get();
}
}
public static void main(final String[] arguments) throws InterruptedException {
final Counter counter = new Counter();
//1000 threads
for(int i = 0; i < 1000 ; i++) {
new Thread(new Runnable() {
public void run() {
counter.increment();
}
}).start();
}
Thread.sleep(6000);
System.out.println("Final number (should be 1000): " + counter.value());
}
}
Spowoduje to następujący wynik.
Wynik
Final number (should be 1000): 1000
Klasa java.util.concurrent.atomic.AtomicBoolean zapewnia operacje na bazowych wartościach logicznych, które mogą być odczytywane i zapisywane atomowo, a także zawiera zaawansowane operacje atomowe. AtomicBoolean obsługuje niepodzielne operacje na bazowej zmiennej boolowskiej. Posiada metody pobierania i ustawiania, które działają jak odczyty i zapisy na zmiennych zmiennych. Oznacza to, że zestaw ma relację zdarzenie przed każdym kolejnym uzyskaniem tej samej zmiennej. Atomowa metoda compareAndSet również ma te funkcje spójności pamięci.
Metody AtomicBoolean
Poniżej znajduje się lista ważnych metod dostępnych w klasie AtomicBoolean.
Sr.No. | Metoda i opis |
---|---|
1 | public boolean compareAndSet(boolean expect, boolean update) Atomowo ustawia wartość na podaną zaktualizowaną wartość, jeśli bieżąca wartość == oczekiwana wartość. |
2 | public boolean get() Zwraca bieżącą wartość. |
3 | public boolean getAndSet(boolean newValue) Atomowo ustawia podaną wartość i zwraca poprzednią wartość. |
4 | public void lazySet(boolean newValue) Ostatecznie ustawia się na podaną wartość. |
5 | public void set(boolean newValue) Bezwarunkowo ustawia podaną wartość. |
6 | public String toString() Zwraca ciąg znaków reprezentujący bieżącą wartość. |
7 | public boolean weakCompareAndSet(boolean expect, boolean update) Atomowo ustawia wartość na podaną zaktualizowaną wartość, jeśli bieżąca wartość == oczekiwana wartość. |
Przykład
Poniższy program TestThread przedstawia użycie zmiennej AtomicBoolean w środowisku opartym na wątkach.
import java.util.concurrent.atomic.AtomicBoolean;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException {
final AtomicBoolean atomicBoolean = new AtomicBoolean(false);
new Thread("Thread 1") {
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName()
+" Waiting for Thread 2 to set Atomic variable to true. Current value is "
+ atomicBoolean.get());
if(atomicBoolean.compareAndSet(true, false)) {
System.out.println("Done!");
break;
}
}
};
}.start();
new Thread("Thread 2") {
public void run() {
System.out.println(Thread.currentThread().getName() +
", Atomic Variable: " +atomicBoolean.get());
System.out.println(Thread.currentThread().getName() +
" is setting the variable to true ");
atomicBoolean.set(true);
System.out.println(Thread.currentThread().getName() +
", Atomic Variable: " +atomicBoolean.get());
};
}.start();
}
}
Spowoduje to następujący wynik.
Wynik
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 2, Atomic Variable: false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 2 is setting the variable to true
Thread 2, Atomic Variable: true
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Done!
Klasa java.util.concurrent.atomic.AtomicReference udostępnia operacje na bazowym odwołaniu do obiektu, które mogą być odczytywane i zapisywane atomowo, a także zawiera zaawansowane operacje atomowe. AtomicReference obsługuje niepodzielne operacje na bazowej zmiennej odwołania do obiektu. Posiada metody pobierania i ustawiania, które działają jak odczyty i zapisy na zmiennych zmiennych. Oznacza to, że zestaw ma relację zdarzenie przed każdym kolejnym uzyskaniem tej samej zmiennej. Atomowa metoda compareAndSet również ma te funkcje spójności pamięci.
Metody AtomicReference
Poniżej znajduje się lista ważnych metod dostępnych w klasie AtomicReference.
Sr.No. | Metoda i opis |
---|---|
1 | public boolean compareAndSet(V expect, V update) Atomowo ustawia wartość na podaną zaktualizowaną wartość, jeśli bieżąca wartość == oczekiwana wartość. |
2 | public boolean get() Zwraca bieżącą wartość. |
3 | public boolean getAndSet(V newValue) Atomowo ustawia podaną wartość i zwraca poprzednią wartość. |
4 | public void lazySet(V newValue) Ostatecznie ustawia się na podaną wartość. |
5 | public void set(V newValue) Bezwarunkowo ustawia podaną wartość. |
6 | public String toString() Zwraca ciąg znaków reprezentujący bieżącą wartość. |
7 | public boolean weakCompareAndSet(V expect, V update) Atomowo ustawia wartość na podaną zaktualizowaną wartość, jeśli bieżąca wartość == oczekiwana wartość. |
Przykład
Poniższy program TestThread przedstawia użycie zmiennej AtomicReference w środowisku opartym na wątkach.
import java.util.concurrent.atomic.AtomicReference;
public class TestThread {
private static String message = "hello";
private static AtomicReference<String> atomicReference;
public static void main(final String[] arguments) throws InterruptedException {
atomicReference = new AtomicReference<String>(message);
new Thread("Thread 1") {
public void run() {
atomicReference.compareAndSet(message, "Thread 1");
message = message.concat("-Thread 1!");
};
}.start();
System.out.println("Message is: " + message);
System.out.println("Atomic Reference of Message is: " + atomicReference.get());
}
}
Spowoduje to następujący wynik.
Wynik
Message is: hello
Atomic Reference of Message is: Thread 1
Klasa java.util.concurrent.atomic.AtomicIntegerArray zapewnia operacje na bazowej tablicy int, która może być odczytywana i zapisywana atomowo, a także zawiera zaawansowane operacje atomowe. AtomicIntegerArray obsługuje niepodzielne operacje na bazowej zmiennej tablicowej int. Posiada metody pobierania i ustawiania, które działają jak odczyty i zapisy na zmiennych zmiennych. Oznacza to, że zestaw ma relację zdarzenie przed każdym kolejnym uzyskaniem tej samej zmiennej. Atomowa metoda compareAndSet również ma te funkcje spójności pamięci.
Metody AtomicIntegerArray
Poniżej znajduje się lista ważnych metod dostępnych w klasie AtomicIntegerArray.
Sr.No. | Metoda i opis |
---|---|
1 | public int addAndGet(int i, int delta) Atomowo dodaje podaną wartość do elementu o indeksie i. |
2 | public boolean compareAndSet(int i, int expect, int update) Atomowo ustawia element na pozycji i na podaną zaktualizowaną wartość, jeśli bieżąca wartość == oczekiwana wartość. |
3 | public int decrementAndGet(int i) Atomowo zmniejsza o jeden element pod indeksem i. |
4 | public int get(int i) Pobiera bieżącą wartość z pozycji i. |
5 | public int getAndAdd(int i, int delta) Atomowo dodaje podaną wartość do elementu o indeksie i. |
6 | public int getAndDecrement(int i) Atomowo zmniejsza o jeden element pod indeksem i. |
7 | public int getAndIncrement(int i) Atomowo zwiększa o jeden element pod indeksem i. |
8 | public int getAndSet(int i, int newValue) Atomowo ustawia element w pozycji i na podaną wartość i zwraca starą wartość. |
9 | public int incrementAndGet(int i) Atomowo zwiększa o jeden element pod indeksem i. |
10 | public void lazySet(int i, int newValue) Ostatecznie ustawia element w pozycji i na podaną wartość. |
11 | public int length() Zwraca długość tablicy. |
12 | public void set(int i, int newValue) Ustawia element w pozycji i na podaną wartość. |
13 | public String toString() Zwraca ciąg znaków reprezentujący bieżące wartości tablicy. |
14 | public boolean weakCompareAndSet(int i, int expect, int update) Atomowo ustawia element na pozycji i na podaną zaktualizowaną wartość, jeśli bieżąca wartość == oczekiwana wartość. |
Przykład
Poniższy program TestThread przedstawia użycie zmiennej AtomicIntegerArray w środowisku opartym na wątkach.
import java.util.concurrent.atomic.AtomicIntegerArray;
public class TestThread {
private static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);
public static void main(final String[] arguments) throws InterruptedException {
for (int i = 0; i<atomicIntegerArray.length(); i++) {
atomicIntegerArray.set(i, 1);
}
Thread t1 = new Thread(new Increment());
Thread t2 = new Thread(new Compare());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Values: ");
for (int i = 0; i<atomicIntegerArray.length(); i++) {
System.out.print(atomicIntegerArray.get(i) + " ");
}
}
static class Increment implements Runnable {
public void run() {
for(int i = 0; i<atomicIntegerArray.length(); i++) {
int add = atomicIntegerArray.incrementAndGet(i);
System.out.println("Thread " + Thread.currentThread().getId()
+ ", index " +i + ", value: "+ add);
}
}
}
static class Compare implements Runnable {
public void run() {
for(int i = 0; i<atomicIntegerArray.length(); i++) {
boolean swapped = atomicIntegerArray.compareAndSet(i, 2, 3);
if(swapped) {
System.out.println("Thread " + Thread.currentThread().getId()
+ ", index " +i + ", value: 3");
}
}
}
}
}
Spowoduje to następujący wynik.
Wynik
Thread 10, index 0, value: 2
Thread 10, index 1, value: 2
Thread 10, index 2, value: 2
Thread 11, index 0, value: 3
Thread 10, index 3, value: 2
Thread 11, index 1, value: 3
Thread 11, index 2, value: 3
Thread 10, index 4, value: 2
Thread 11, index 3, value: 3
Thread 10, index 5, value: 2
Thread 10, index 6, value: 2
Thread 11, index 4, value: 3
Thread 10, index 7, value: 2
Thread 11, index 5, value: 3
Thread 10, index 8, value: 2
Thread 11, index 6, value: 3
Thread 10, index 9, value: 2
Thread 11, index 7, value: 3
Thread 11, index 8, value: 3
Thread 11, index 9, value: 3
Values:
3 3 3 3 3 3 3 3 3 3
Klasa java.util.concurrent.atomic.AtomicLongArray zapewnia operacje na bazowej długiej tablicy, które mogą być odczytywane i zapisywane atomowo, a także zawiera zaawansowane operacje atomowe. AtomicLongArray obsługuje niepodzielne operacje na bazowej zmiennej tablicy. Posiada metody pobierania i ustawiania, które działają jak odczyty i zapisy na zmiennych zmiennych. Oznacza to, że zestaw ma relację zdarzenie przed każdym kolejnym uzyskaniem tej samej zmiennej. Atomowa metoda compareAndSet również ma te funkcje spójności pamięci.
Metody AtomicLongArray
Poniżej znajduje się lista ważnych metod dostępnych w klasie AtomicLongArray.
Sr.No. | Metoda i opis |
---|---|
1 | public long addAndGet(int i, long delta) Atomowo dodaje podaną wartość do elementu o indeksie i. |
2 | public boolean compareAndSet(int i, long expect, long update) Atomowo ustawia element na pozycji i na podaną zaktualizowaną wartość, jeśli bieżąca wartość == oczekiwana wartość. |
3 | public long decrementAndGet(int i) Atomowo zmniejsza o jeden element pod indeksem i. |
4 | public long get(int i) Pobiera bieżącą wartość z pozycji i. |
5 | public long getAndAdd(int i, long delta) Atomowo dodaje podaną wartość do elementu o indeksie i. |
6 | public long getAndDecrement(int i) Atomowo zmniejsza o jeden element pod indeksem i. |
7 | public long getAndIncrement(int i) Atomowo zwiększa o jeden element pod indeksem i. |
8 | public long getAndSet(int i, long newValue) Atomowo ustawia element w pozycji i na podaną wartość i zwraca starą wartość. |
9 | public long incrementAndGet(int i) Atomowo zwiększa o jeden element pod indeksem i. |
10 | public void lazySet(int i, long newValue) Ostatecznie ustawia element w pozycji i na podaną wartość. |
11 | public int length() Zwraca długość tablicy. |
12 | public void set(int i, long newValue) Ustawia element w pozycji i na podaną wartość. |
13 | public String toString() Zwraca ciąg znaków reprezentujący bieżące wartości tablicy. |
14 | public boolean weakCompareAndSet(int i, long expect, long update) Atomowo ustawia element na pozycji i na podaną zaktualizowaną wartość, jeśli bieżąca wartość == oczekiwana wartość. |
Przykład
Poniższy program TestThread przedstawia użycie zmiennej AtomicIntegerArray w środowisku opartym na wątkach.
import java.util.concurrent.atomic.AtomicLongArray;
public class TestThread {
private static AtomicLongArray atomicLongArray = new AtomicLongArray(10);
public static void main(final String[] arguments) throws InterruptedException {
for (int i = 0; i<atomicLongArray.length(); i++) {
atomicLongArray.set(i, 1);
}
Thread t1 = new Thread(new Increment());
Thread t2 = new Thread(new Compare());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Values: ");
for (int i = 0; i<atomicLongArray.length(); i++) {
System.out.print(atomicLongArray.get(i) + " ");
}
}
static class Increment implements Runnable {
public void run() {
for(int i = 0; i<atomicLongArray.length(); i++) {
long add = atomicLongArray.incrementAndGet(i);
System.out.println("Thread " + Thread.currentThread().getId()
+ ", index " +i + ", value: "+ add);
}
}
}
static class Compare implements Runnable {
public void run() {
for(int i = 0; i<atomicLongArray.length(); i++) {
boolean swapped = atomicLongArray.compareAndSet(i, 2, 3);
if(swapped) {
System.out.println("Thread " + Thread.currentThread().getId()
+ ", index " +i + ", value: 3");
}
}
}
}
}
Spowoduje to następujący wynik.
Wynik
Thread 9, index 0, value: 2
Thread 10, index 0, value: 3
Thread 9, index 1, value: 2
Thread 9, index 2, value: 2
Thread 9, index 3, value: 2
Thread 9, index 4, value: 2
Thread 10, index 1, value: 3
Thread 9, index 5, value: 2
Thread 10, index 2, value: 3
Thread 9, index 6, value: 2
Thread 10, index 3, value: 3
Thread 9, index 7, value: 2
Thread 10, index 4, value: 3
Thread 9, index 8, value: 2
Thread 9, index 9, value: 2
Thread 10, index 5, value: 3
Thread 10, index 6, value: 3
Thread 10, index 7, value: 3
Thread 10, index 8, value: 3
Thread 10, index 9, value: 3
Values:
3 3 3 3 3 3 3 3 3 3
Klasa java.util.concurrent.atomic.AtomicReferenceArray udostępnia operacje na bazowej tablicy referencyjnej, która może być odczytywana i zapisywana atomowo, a także zawiera zaawansowane operacje atomowe. AtomicReferenceArray obsługuje niepodzielne operacje na źródłowej zmiennej tablicy referencyjnej. Posiada metody pobierania i ustawiania, które działają jak odczyty i zapisy na zmiennych zmiennych. Oznacza to, że zestaw ma relację zdarzenie przed każdym kolejnym uzyskaniem tej samej zmiennej. Atomowa metoda compareAndSet również ma te funkcje spójności pamięci.
Metody AtomicReferenceArray
Poniżej znajduje się lista ważnych metod dostępnych w klasie AtomicReferenceArray.
Sr.No. | Metoda i opis |
---|---|
1 | public boolean compareAndSet(int i, E expect, E update) Atomowo ustawia element na pozycji i na podaną zaktualizowaną wartość, jeśli bieżąca wartość == oczekiwana wartość. |
2 | public E get(int i) Pobiera bieżącą wartość z pozycji i. |
3 | public E getAndSet(int i, E newValue) Atomowo ustawia element w pozycji i na podaną wartość i zwraca starą wartość. |
4 | public void lazySet(int i, E newValue) Ostatecznie ustawia element w pozycji i na podaną wartość. |
5 | public int length() Zwraca długość tablicy. |
6 | public void set(int i, E newValue) Ustawia element w pozycji i na podaną wartość. |
7 | public String toString() Zwraca ciąg znaków reprezentujący bieżące wartości tablicy. |
8 | public boolean weakCompareAndSet(int i, E expect, E update) Atomowo ustawia element na pozycji i na podaną zaktualizowaną wartość, jeśli bieżąca wartość == oczekiwana wartość. |
Przykład
Poniższy program TestThread przedstawia użycie zmiennej AtomicReferenceArray w środowisku opartym na wątkach.
import java.util.concurrent.atomic.AtomicReferenceArray;
public class TestThread {
private static String[] source = new String[10];
private static AtomicReferenceArray<String> atomicReferenceArray
= new AtomicReferenceArray<String>(source);
public static void main(final String[] arguments) throws InterruptedException {
for (int i = 0; i<atomicReferenceArray.length(); i++) {
atomicReferenceArray.set(i, "item-2");
}
Thread t1 = new Thread(new Increment());
Thread t2 = new Thread(new Compare());
t1.start();
t2.start();
t1.join();
t2.join();
}
static class Increment implements Runnable {
public void run() {
for(int i = 0; i<atomicReferenceArray.length(); i++) {
String add = atomicReferenceArray.getAndSet(i,"item-"+ (i+1));
System.out.println("Thread " + Thread.currentThread().getId()
+ ", index " +i + ", value: "+ add);
}
}
}
static class Compare implements Runnable {
public void run() {
for(int i = 0; i<atomicReferenceArray.length(); i++) {
System.out.println("Thread " + Thread.currentThread().getId()
+ ", index " +i + ", value: "+ atomicReferenceArray.get(i));
boolean swapped = atomicReferenceArray.compareAndSet(i, "item-2", "updated-item-2");
System.out.println("Item swapped: " + swapped);
if(swapped) {
System.out.println("Thread " + Thread.currentThread().getId()
+ ", index " +i + ", updated-item-2");
}
}
}
}
}
Spowoduje to następujący wynik.
Wynik
Thread 9, index 0, value: item-2
Thread 10, index 0, value: item-1
Item swapped: false
Thread 10, index 1, value: item-2
Item swapped: true
Thread 9, index 1, value: updated-item-2
Thread 10, index 1, updated-item-2
Thread 10, index 2, value: item-3
Item swapped: false
Thread 10, index 3, value: item-2
Item swapped: true
Thread 10, index 3, updated-item-2
Thread 10, index 4, value: item-2
Item swapped: true
Thread 10, index 4, updated-item-2
Thread 10, index 5, value: item-2
Item swapped: true
Thread 10, index 5, updated-item-2
Thread 10, index 6, value: item-2
Thread 9, index 2, value: item-2
Item swapped: true
Thread 9, index 3, value: updated-item-2
Thread 10, index 6, updated-item-2
Thread 10, index 7, value: item-2
Thread 9, index 4, value: updated-item-2
Item swapped: true
Thread 9, index 5, value: updated-item-2
Thread 10, index 7, updated-item-2
Thread 9, index 6, value: updated-item-2
Thread 10, index 8, value: item-2
Thread 9, index 7, value: updated-item-2
Item swapped: true
Thread 9, index 8, value: updated-item-2
Thread 10, index 8, updated-item-2
Thread 9, index 9, value: item-2
Thread 10, index 9, value: item-10
Item swapped: false
Interfejs java.util.concurrent.Executor to prosty interfejs wspierający uruchamianie nowych zadań.
Metody ExecutorService
Sr.No. | Metoda i opis |
---|---|
1 | void execute(Runnable command) Wykonuje podaną komendę w przyszłości. |
Przykład
Poniższy program TestThread przedstawia użycie interfejsu Executora w środowisku opartym na wątkach.
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException {
Executor executor = Executors.newCachedThreadPool();
executor.execute(new Task());
ThreadPoolExecutor pool = (ThreadPoolExecutor)executor;
pool.shutdown();
}
static class Task implements Runnable {
public void run() {
try {
Long duration = (long) (Math.random() * 5);
System.out.println("Running Task!");
TimeUnit.SECONDS.sleep(duration);
System.out.println("Task Completed");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Spowoduje to następujący wynik.
Wynik
Running Task!
Task Completed
Interfejs java.util.concurrent.ExecutorService jest podinterfejsem interfejsu Executora i dodaje funkcje do zarządzania cyklem życia, zarówno poszczególnych zadań, jak i samego modułu wykonawczego.
Metody ExecutorService
Sr.No. | Metoda i opis |
---|---|
1 | boolean awaitTermination(long timeout, TimeUnit unit) Blokuje do momentu zakończenia wykonywania wszystkich zadań po żądaniu zamknięcia systemu, przekroczenia limitu czasu lub przerwania bieżącego wątku, w zależności od tego, co nastąpi wcześniej. |
2 | <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) Wykonuje podane zadania, zwracając listę kontraktów futures posiadających status i wyniki po wykonaniu wszystkich. |
3 | <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) Wykonuje podane zadania, zwracając listę kontraktów futures posiadających swój status i wyniki po wykonaniu wszystkich zadań lub po upływie limitu czasu, w zależności od tego, co nastąpi wcześniej. |
4 | <T> T invokeAny(Collection<? extends Callable<T>> tasks) Wykonuje podane zadania, zwracając wynik zadania, które zakończyło się pomyślnie (tj. Bez zgłaszania wyjątku), jeśli takie istnieją. |
5 | <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) Wykonuje zadane zadania, zwracając wynik zadania, które zakończyło się pomyślnie (tj. Bez zgłaszania wyjątku), jeśli któreś z nich zrobi przed upływem określonego czasu. |
6 | boolean isShutdown() Zwraca wartość true, jeśli ten moduł wykonawczy został zamknięty. |
7 | boolean isTerminated() Zwraca wartość true, jeśli wszystkie zadania zostały zakończone po zamknięciu. |
8 | void shutdown() Inicjuje uporządkowane zamknięcie, w którym wykonywane są wcześniej przesłane zadania, ale żadne nowe zadania nie będą akceptowane. |
9 | List<Runnable> shutdownNow() Próbuje zatrzymać wszystkie aktywnie wykonywane zadania, zatrzymuje przetwarzanie zadań oczekujących i zwraca listę zadań oczekujących na wykonanie. |
10 | <T> Future<T> submit(Callable<T> task) Wysyła zadanie zwracające wartość do wykonania i zwraca Future reprezentującą oczekujące wyniki zadania. |
11 | Future<?> submit(Runnable task) Wysyła zadanie do wykonania do wykonania i zwraca przyszłość reprezentującą to zadanie. |
12 | <T> Future<T> submit(Runnable task, T result) Wysyła zadanie do wykonania do wykonania i zwraca przyszłość reprezentującą to zadanie. |
Przykład
Poniższy program TestThread przedstawia użycie interfejsu ExecutorService w środowisku opartym na wątkach.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
executor.submit(new Task());
System.out.println("Shutdown executor");
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.err.println("tasks interrupted");
} finally {
if (!executor.isTerminated()) {
System.err.println("cancel non-finished tasks");
}
executor.shutdownNow();
System.out.println("shutdown finished");
}
}
static class Task implements Runnable {
public void run() {
try {
Long duration = (long) (Math.random() * 20);
System.out.println("Running Task!");
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Spowoduje to następujący wynik.
Wynik
Shutdown executor
Running Task!
shutdown finished
cancel non-finished tasks
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:302)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:328)
at TestThread$Task.run(TestThread.java:39)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:662)
Interfejs java.util.concurrent.ScheduledExecutorService jest podinterfejsem interfejsu ExecutorService i obsługuje przyszłe i / lub okresowe wykonywanie zadań.
Metody ScheduledExecutorService
Sr.No. | Metoda i opis |
---|---|
1 | <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) Tworzy i wykonuje ScheduledFuture, która jest włączana po określonym opóźnieniu. |
2 | ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) Tworzy i wykonuje jednorazową akcję, która staje się aktywna po określonym opóźnieniu. |
3 | ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) Tworzy i wykonuje okresowe działanie, które jest włączane najpierw po zadanym początkowym opóźnieniu, a następnie po zadanym okresie; to znaczy, że wykonanie rozpocznie się po initialDelay, a następnie initialDelay + okres, a następnie initialDelay + 2 * okres i tak dalej. |
4 | ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) Tworzy i wykonuje okresowe działanie, które jest włączane najpierw po zadanym początkowym opóźnieniu, a następnie z zadanym opóźnieniem między zakończeniem jednego wykonania a rozpoczęciem następnego. |
Przykład
Poniższy program TestThread przedstawia użycie interfejsu ScheduledExecutorService w środowisku opartym na wątkach.
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException {
final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
final ScheduledFuture<?> beepHandler =
scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);
scheduler.schedule(new Runnable() {
@Override
public void run() {
beepHandler.cancel(true);
scheduler.shutdown();
}
}, 10, TimeUnit.SECONDS);
}
static class BeepTask implements Runnable {
public void run() {
System.out.println("beep");
}
}
}
Spowoduje to następujący wynik.
Wynik
beep
beep
beep
beep
Stałą pulę wątków można uzyskać, wywołując statyczną metodę newFixedThreadPool () klasy Executors.
Składnia
ExecutorService fixedPool = Executors.newFixedThreadPool(2);
gdzie
Maksymalnie 2 wątki będą aktywne do przetwarzania zadań.
Jeśli przesłano więcej niż 2 wątki, są one zatrzymywane w kolejce do czasu, gdy będą dostępne.
Nowy wątek jest tworzony, aby zająć jego miejsce, jeśli wątek zostanie zakończony z powodu niepowodzenia podczas wykonywania zamknięcia programu wykonawczego nie zostało jeszcze wywołane.
Każdy wątek istnieje do momentu zamknięcia puli.
Przykład
Poniższy program TestThread przedstawia użycie metody newFixedThreadPool w środowisku opartym na wątkach.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(2);
// Cast the object to its class type
ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;
//Stats before tasks execution
System.out.println("Largest executions: "
+ pool.getLargestPoolSize());
System.out.println("Maximum allowed threads: "
+ pool.getMaximumPoolSize());
System.out.println("Current threads in pool: "
+ pool.getPoolSize());
System.out.println("Currently executing threads: "
+ pool.getActiveCount());
System.out.println("Total number of threads(ever scheduled): "
+ pool.getTaskCount());
executor.submit(new Task());
executor.submit(new Task());
//Stats after tasks execution
System.out.println("Core threads: " + pool.getCorePoolSize());
System.out.println("Largest executions: "
+ pool.getLargestPoolSize());
System.out.println("Maximum allowed threads: "
+ pool.getMaximumPoolSize());
System.out.println("Current threads in pool: "
+ pool.getPoolSize());
System.out.println("Currently executing threads: "
+ pool.getActiveCount());
System.out.println("Total number of threads(ever scheduled): "
+ pool.getTaskCount());
executor.shutdown();
}
static class Task implements Runnable {
public void run() {
try {
Long duration = (long) (Math.random() * 5);
System.out.println("Running Task! Thread Name: " +
Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(duration);
System.out.println("Task Completed! Thread Name: " +
Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Spowoduje to następujący wynik.
Wynik
Largest executions: 0
Maximum allowed threads: 2
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 2
Largest executions: 2
Maximum allowed threads: 2
Current threads in pool: 2
Currently executing threads: 1
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-1
Running Task! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-1
Buforowaną pulę wątków można uzyskać, wywołując statyczną metodę newCachedThreadPool () klasy Executors.
Składnia
ExecutorService executor = Executors.newCachedThreadPool();
gdzie
Metoda newCachedThreadPool tworzy moduł wykonawczy mający rozszerzalną pulę wątków.
Taki wykonawca jest odpowiedni dla aplikacji, które uruchamiają wiele krótkotrwałych zadań.
Przykład
Poniższy program TestThread przedstawia użycie metody newCachedThreadPool w środowisku opartym na wątkach.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
// Cast the object to its class type
ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;
//Stats before tasks execution
System.out.println("Largest executions: "
+ pool.getLargestPoolSize());
System.out.println("Maximum allowed threads: "
+ pool.getMaximumPoolSize());
System.out.println("Current threads in pool: "
+ pool.getPoolSize());
System.out.println("Currently executing threads: "
+ pool.getActiveCount());
System.out.println("Total number of threads(ever scheduled): "
+ pool.getTaskCount());
executor.submit(new Task());
executor.submit(new Task());
//Stats after tasks execution
System.out.println("Core threads: " + pool.getCorePoolSize());
System.out.println("Largest executions: "
+ pool.getLargestPoolSize());
System.out.println("Maximum allowed threads: "
+ pool.getMaximumPoolSize());
System.out.println("Current threads in pool: "
+ pool.getPoolSize());
System.out.println("Currently executing threads: "
+ pool.getActiveCount());
System.out.println("Total number of threads(ever scheduled): "
+ pool.getTaskCount());
executor.shutdown();
}
static class Task implements Runnable {
public void run() {
try {
Long duration = (long) (Math.random() * 5);
System.out.println("Running Task! Thread Name: " +
Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(duration);
System.out.println("Task Completed! Thread Name: " +
Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Spowoduje to następujący wynik.
Wynik
Largest executions: 0
Maximum allowed threads: 2147483647
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 0
Largest executions: 2
Maximum allowed threads: 2147483647
Current threads in pool: 2
Currently executing threads: 2
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-1
Running Task! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-1
Zaplanowaną pulę wątków można uzyskać, wywołując statyczną metodę newScheduledThreadPool () klasy Executors.
Składnia
ExecutorService executor = Executors.newScheduledThreadPool(1);
Przykład
Poniższy program TestThread pokazuje użycie metody newScheduledThreadPool w środowisku opartym na wątkach.
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException {
final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
final ScheduledFuture<?> beepHandler =
scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);
scheduler.schedule(new Runnable() {
@Override
public void run() {
beepHandler.cancel(true);
scheduler.shutdown();
}
}, 10, TimeUnit.SECONDS);
}
static class BeepTask implements Runnable {
public void run() {
System.out.println("beep");
}
}
}
Spowoduje to następujący wynik.
Wynik
beep
beep
beep
beep
Pojedynczą pulę wątków można uzyskać, wywołując statyczną metodę newSingleThreadExecutor () klasy Executors.
Składnia
ExecutorService executor = Executors.newSingleThreadExecutor();
Gdzie metoda newSingleThreadExecutor tworzy moduł wykonawczy, który wykonuje pojedyncze zadanie na raz.
Przykład
Poniższy program TestThread przedstawia użycie metody newSingleThreadExecutor w środowisku opartym na wątkach.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
executor.submit(new Task());
System.out.println("Shutdown executor");
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.err.println("tasks interrupted");
} finally {
if (!executor.isTerminated()) {
System.err.println("cancel non-finished tasks");
}
executor.shutdownNow();
System.out.println("shutdown finished");
}
}
static class Task implements Runnable {
public void run() {
try {
Long duration = (long) (Math.random() * 20);
System.out.println("Running Task!");
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Spowoduje to następujący wynik.
Wynik
Shutdown executor
Running Task!
shutdown finished
cancel non-finished tasks
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:302)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:328)
at TestThread$Task.run(TestThread.java:39) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:662)
java.util.concurrent.ThreadPoolExecutor to usługa ExecutorService służąca do wykonywania każdego przesłanego zadania przy użyciu jednego z kilku wątków w puli, zwykle konfigurowanych przy użyciu metod fabrycznych modułów wykonawczych. Udostępnia również różne metody narzędziowe do sprawdzania aktualnych statystyk wątków i kontrolowania ich.
Metody ThreadPoolExecutor
Sr.No. | Metoda i opis |
---|---|
1 | protected void afterExecute(Runnable r, Throwable t) Metoda wywoływana po zakończeniu wykonywania danego Runnable. |
2 | void allowCoreThreadTimeOut(boolean value) Ustawia zasady określające, czy główne wątki mogą przekroczyć limit czasu i zakończyć się, jeśli żadne zadania nie nadejdą w czasie utrzymywania aktywności, i są zastępowane w razie potrzeby, gdy nadejdą nowe zadania. |
3 | boolean allowsCoreThreadTimeOut() Zwraca wartość true, jeśli ta pula pozwala wątkom rdzeniowym na przekroczenie limitu czasu i zakończenie, jeśli żadne zadania nie nadejdą w czasie keepAlive, i są zastępowane w razie potrzeby, gdy nadejdą nowe zadania. |
4 | boolean awaitTermination(long timeout, TimeUnit unit) Blokuje do momentu zakończenia wykonywania wszystkich zadań po żądaniu zamknięcia systemu, przekroczenia limitu czasu lub przerwania bieżącego wątku, w zależności od tego, co nastąpi wcześniej. |
5 | protected void beforeExecute(Thread t, Runnable r) Metoda wywołana przed wykonaniem danego Runnable w danym wątku. |
6 | void execute(Runnable command) Wykonuje dane zadanie w przyszłości. |
7 | protected void finalize() Wywołuje zamknięcie, gdy ten moduł wykonawczy nie jest już przywoływany i nie ma wątków. |
8 | int getActiveCount() Zwraca przybliżoną liczbę wątków, które aktywnie wykonują zadania. |
9 | long getCompletedTaskCount() Zwraca przybliżoną całkowitą liczbę zadań, które zakończyły wykonywanie. |
10 | int getCorePoolSize() Zwraca podstawową liczbę wątków. |
11 | long getKeepAliveTime(TimeUnit unit) Zwraca czas utrzymywania aktywności wątku, czyli czas, przez który wątki przekraczające rozmiar puli rdzenia mogą pozostawać w stanie bezczynności przed zakończeniem. |
12 | int getLargestPoolSize() Zwraca największą liczbę wątków, które kiedykolwiek znajdowały się jednocześnie w puli. |
13 | int getMaximumPoolSize() Zwraca maksymalną dozwoloną liczbę wątków. |
14 | int getPoolSize() Zwraca bieżącą liczbę wątków w puli. |
15 | BlockingQueue
Zwraca kolejkę zadań używaną przez ten moduł wykonawczy. |
15 | RejectedExecutionHandler getRejectedExecutionHandler() Zwraca bieżącą procedurę obsługi dla niewykonalnych zadań. |
16 | long getTaskCount() Zwraca przybliżoną całkowitą liczbę zadań, które kiedykolwiek zostały zaplanowane do wykonania. |
17 | ThreadFactory getThreadFactory() Zwraca fabrykę wątków używaną do tworzenia nowych wątków. |
18 | boolean isShutdown() Zwraca wartość true, jeśli ten moduł wykonawczy został zamknięty. |
19 | boolean isTerminated() Zwraca wartość true, jeśli wszystkie zadania zostały zakończone po zamknięciu. |
20 | boolean isTerminating() Zwraca wartość true, jeśli ten moduł wykonujący jest w trakcie kończenia działania po shutdown () lub shutdownNow (), ale nie został całkowicie zakończony. |
21 | int prestartAllCoreThreads() Uruchamia wszystkie podstawowe wątki, powodując, że bezczynnie czekają na pracę. |
22 | boolean prestartCoreThread() Uruchamia główny wątek, powodując bezczynne oczekiwanie na pracę. |
23 | void purge() Próbuje usunąć z kolejki roboczej wszystkie zadania w przyszłości, które zostały anulowane. |
24 | boolean remove(Runnable task) Usuwa to zadanie z wewnętrznej kolejki executora, jeśli jest obecne, powodując w ten sposób, że nie jest uruchamiane, jeśli nie zostało jeszcze uruchomione. |
25 | void setCorePoolSize(int corePoolSize) Ustawia podstawową liczbę wątków. |
26 | void setKeepAliveTime(long time, TimeUnit unit) Ustawia limit czasu, przez który wątki mogą pozostawać bezczynne przed zakończeniem. |
27 | void setMaximumPoolSize(int maximumPoolSize) Ustawia maksymalną dozwoloną liczbę wątków. |
28 | void setRejectedExecutionHandler(RejectedExecutionHandler handler) Ustawia nową procedurę obsługi dla niewykonalnych zadań. |
29 | void setThreadFactory(ThreadFactory threadFactory) Ustawia fabrykę gwintów używaną do tworzenia nowych wątków. |
30 | void shutdown() Inicjuje uporządkowane zamknięcie, w którym wykonywane są wcześniej przesłane zadania, ale żadne nowe zadania nie będą akceptowane. |
31 | List<Runnable> shutdownNow() Próbuje zatrzymać wszystkie aktywnie wykonywane zadania, zatrzymuje przetwarzanie zadań oczekujących i zwraca listę zadań oczekujących na wykonanie. |
32 | protected void terminated() Metoda wywoływana po zakończeniu działania modułu Executor. |
33 | String toString() Zwraca ciąg identyfikujący tę pulę, a także jej stan, w tym wskazania stanu uruchomienia oraz szacowaną liczbę pracowników i zadań. |
Przykład
Poniższy program TestThread przedstawia użycie interfejsu ThreadPoolExecutor w środowisku opartym na wątkach.
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException {
ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newCachedThreadPool();
//Stats before tasks execution
System.out.println("Largest executions: "
+ executor.getLargestPoolSize());
System.out.println("Maximum allowed threads: "
+ executor.getMaximumPoolSize());
System.out.println("Current threads in pool: "
+ executor.getPoolSize());
System.out.println("Currently executing threads: "
+ executor.getActiveCount());
System.out.println("Total number of threads(ever scheduled): "
+ executor.getTaskCount());
executor.submit(new Task());
executor.submit(new Task());
//Stats after tasks execution
System.out.println("Core threads: " + executor.getCorePoolSize());
System.out.println("Largest executions: "
+ executor.getLargestPoolSize());
System.out.println("Maximum allowed threads: "
+ executor.getMaximumPoolSize());
System.out.println("Current threads in pool: "
+ executor.getPoolSize());
System.out.println("Currently executing threads: "
+ executor.getActiveCount());
System.out.println("Total number of threads(ever scheduled): "
+ executor.getTaskCount());
executor.shutdown();
}
static class Task implements Runnable {
public void run() {
try {
Long duration = (long) (Math.random() * 5);
System.out.println("Running Task! Thread Name: " +
Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(duration);
System.out.println("Task Completed! Thread Name: " +
Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Spowoduje to następujący wynik.
Wynik
Largest executions: 0
Maximum allowed threads: 2147483647
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 0
Largest executions: 2
Maximum allowed threads: 2147483647
Current threads in pool: 2
Currently executing threads: 2
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-2
Running Task! Thread Name: pool-1-thread-1
Task Completed! Thread Name: pool-1-thread-1
Task Completed! Thread Name: pool-1-thread-2
java.util.concurrent.ScheduledThreadPoolExecutor jest podklasą ThreadPoolExecutor i może dodatkowo zaplanować uruchamianie poleceń po określonym czasie lub okresowe wykonywanie.
Metody ScheduledThreadPoolExecutor
Sr.No. | Metoda i opis |
---|---|
1 | protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task) Modyfikuje lub zastępuje zadanie używane do wykonywania wywoływanego pliku. |
2 | protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) Modyfikuje lub zastępuje zadanie używane do wykonywania pliku wykonywalnego. |
3 | void execute(Runnable command) Wykonuje polecenie z zerowym wymaganym opóźnieniem. |
4 | boolean getContinueExistingPeriodicTasksAfterShutdownPolicy() Pobiera zasady dotyczące kontynuowania wykonywania istniejących zadań okresowych, nawet jeśli ten moduł wykonawczy został zamknięty. |
5 | boolean getExecuteExistingDelayedTasksAfterShutdownPolicy() Pobiera zasady dotyczące wykonywania istniejących opóźnionych zadań, nawet jeśli ten moduł wykonawczy został zamknięty. |
6 | BlockingQueue<Runnable> getQueue() Zwraca kolejkę zadań używaną przez ten moduł wykonawczy. |
7 | boolean getRemoveOnCancelPolicy() Pobiera zasady dotyczące tego, czy anulowane zadania powinny być natychmiast usuwane z kolejki pracy w momencie anulowania. |
8 | <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) Tworzy i wykonuje ScheduledFuture, która jest włączana po określonym opóźnieniu. |
9 | ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) Tworzy i wykonuje jednorazową akcję, która staje się aktywna po określonym opóźnieniu. |
10 | ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) Tworzy i wykonuje okresowe działanie, które jest włączane najpierw po zadanym początkowym opóźnieniu, a następnie po zadanym okresie; to znaczy, że wykonanie rozpocznie się po initialDelay, a następnie initialDelay + okres, a następnie initialDelay + 2 * okres i tak dalej. |
11 | ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) Tworzy i wykonuje okresowe działanie, które jest włączane najpierw po zadanym początkowym opóźnieniu, a następnie z zadanym opóźnieniem między zakończeniem jednego wykonania a rozpoczęciem następnego. |
12 | void setContinueExistingPeriodicTasksAfterShutdownPolicy (boolean value) Ustawia zasady dotyczące kontynuowania wykonywania istniejących zadań okresowych, nawet jeśli ten moduł wykonawczy został zamknięty. |
13 | void setExecuteExistingDelayedTasksAfterShutdownPolicy (boolean value) Ustawia zasady dotyczące wykonywania istniejących opóźnionych zadań, nawet jeśli ten moduł wykonawczy został zamknięty. |
14 | void setRemoveOnCancelPolicy(boolean value) Ustawia zasady określające, czy anulowane zadania powinny być natychmiast usuwane z kolejki pracy w momencie anulowania. |
15 | void shutdown() Inicjuje uporządkowane zamknięcie, w którym wykonywane są wcześniej przesłane zadania, ale żadne nowe zadania nie będą akceptowane. |
16 | List<Runnable> shutdownNow() Próbuje zatrzymać wszystkie aktywnie wykonywane zadania, zatrzymuje przetwarzanie zadań oczekujących i zwraca listę zadań oczekujących na wykonanie. |
17 | <T> Future<T> submit(Callable<T> task) Wysyła zadanie zwracające wartość do wykonania i zwraca Future reprezentującą oczekujące wyniki zadania. |
18 | Future<?> submit(Runnable task) Wysyła zadanie do wykonania do wykonania i zwraca przyszłość reprezentującą to zadanie. |
19 | <T> Future<T> submit(Runnable task, T result) Wysyła zadanie do wykonania do wykonania i zwraca przyszłość reprezentującą to zadanie. |
Przykład
Poniższy program TestThread przedstawia użycie interfejsu ScheduledThreadPoolExecutor w środowisku opartym na wątkach.
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException {
final ScheduledThreadPoolExecutor scheduler =
(ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(1);
final ScheduledFuture<?> beepHandler =
scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);
scheduler.schedule(new Runnable() {
@Override
public void run() {
beepHandler.cancel(true);
scheduler.shutdown();
}
}, 10, TimeUnit.SECONDS);
}
static class BeepTask implements Runnable {
public void run() {
System.out.println("beep");
}
}
}
Spowoduje to następujący wynik.
Wynik
beep
beep
beep
beep
Obiekt java.util.concurrent.Callable może zwrócić obliczony wynik wykonany przez wątek w przeciwieństwie do uruchamialnego interfejsu, który może uruchomić tylko wątek. Obiekt Callable zwraca obiekt Future, który udostępnia metody do monitorowania postępu zadania wykonywanego przez wątek. Obiekt przyszłości może być użyty do sprawdzenia statusu wywoływanego obiektu, a następnie pobrać wynik z wywoływanego po zakończeniu wątku. Zapewnia również funkcjonalność limitu czasu.
Składnia
//submit the callable using ThreadExecutor
//and get the result as a Future object
Future<Long> result10 = executor.submit(new FactorialService(10));
//get the result using get method of the Future object
//get method waits till the thread execution and then return the result of the execution.
Long factorial10 = result10.get();
Przykład
Poniższy program TestThread pokazuje użycie kontraktów futures i wywołań w środowisku opartym na wątkach.
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException,
ExecutionException {
ExecutorService executor = Executors.newSingleThreadExecutor();
System.out.println("Factorial Service called for 10!");
Future<Long> result10 = executor.submit(new FactorialService(10));
System.out.println("Factorial Service called for 20!");
Future<Long> result20 = executor.submit(new FactorialService(20));
Long factorial10 = result10.get();
System.out.println("10! = " + factorial10);
Long factorial20 = result20.get();
System.out.println("20! = " + factorial20);
executor.shutdown();
}
static class FactorialService implements Callable<Long> {
private int number;
public FactorialService(int number) {
this.number = number;
}
@Override
public Long call() throws Exception {
return factorial();
}
private Long factorial() throws InterruptedException {
long result = 1;
while (number != 0) {
result = number * result;
number--;
Thread.sleep(100);
}
return result;
}
}
}
Spowoduje to następujący wynik.
Wynik
Factorial Service called for 10!
Factorial Service called for 20!
10! = 3628800
20! = 2432902008176640000
Struktura rozwidlonych połączeń umożliwia przerwanie określonego zadania kilku pracowników, a następnie oczekiwanie na ich połączenie. W dużym stopniu wykorzystuje możliwości maszyny wieloprocesorowej. Poniżej przedstawiono podstawowe koncepcje i obiekty używane w ramach łączenia rozwidlonego.
Widelec
Rozwidlenie to proces, w którym zadanie dzieli się na mniejsze i niezależne pod-zadania, które mogą być wykonywane jednocześnie.
Składnia
Sum left = new Sum(array, low, mid);
left.fork();
Tutaj Sum jest podklasą RecursiveTask i left.fork () dzieli to zadanie na pod-zadania.
Przystąp
Dołącz to proces, w którym zadanie łączy wszystkie wyniki podzadań po zakończeniu wykonywania podzadań, w przeciwnym razie będzie czekało.
Składnia
left.join();
Tutaj po lewej jest obiekt klasy Sum.
ForkJoinPool
jest to specjalna pula wątków zaprojektowana do pracy z podziałem zadań typu rozwidlanie i łączenie.
Składnia
ForkJoinPool forkJoinPool = new ForkJoinPool(4);
Tutaj nowy ForkJoinPool z poziomem równoległości 4 procesorów.
RecursiveAction
RecursiveAction reprezentuje zadanie, które nie zwraca żadnej wartości.
Składnia
class Writer extends RecursiveAction {
@Override
protected void compute() { }
}
RecursiveTask
RecursiveTask reprezentuje zadanie, które zwraca wartość.
Składnia
class Sum extends RecursiveTask<Long> {
@Override
protected Long compute() { return null; }
}
Przykład
Poniższy program TestThread przedstawia użycie platformy Fork-Join w środowisku opartym na wątkach.
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException,
ExecutionException {
int nThreads = Runtime.getRuntime().availableProcessors();
System.out.println(nThreads);
int[] numbers = new int[1000];
for(int i = 0; i < numbers.length; i++) {
numbers[i] = i;
}
ForkJoinPool forkJoinPool = new ForkJoinPool(nThreads);
Long result = forkJoinPool.invoke(new Sum(numbers,0,numbers.length));
System.out.println(result);
}
static class Sum extends RecursiveTask<Long> {
int low;
int high;
int[] array;
Sum(int[] array, int low, int high) {
this.array = array;
this.low = low;
this.high = high;
}
protected Long compute() {
if(high - low <= 10) {
long sum = 0;
for(int i = low; i < high; ++i)
sum += array[i];
return sum;
} else {
int mid = low + (high - low) / 2;
Sum left = new Sum(array, low, mid);
Sum right = new Sum(array, mid, high);
left.fork();
long rightResult = right.compute();
long leftResult = left.join();
return leftResult + rightResult;
}
}
}
}
Spowoduje to następujący wynik.
Wynik
32
499500
Interfejs java.util.concurrent.BlockingQueue jest podinterfejsem interfejsu Queue i dodatkowo obsługuje takie operacje, jak oczekiwanie, aż kolejka stanie się niepusta przed pobraniem elementu, oraz oczekiwanie na zwolnienie miejsca w kolejce przed zapisaniem elementu .
BlockingQueue Methods
Sr.No. | Metoda i opis |
---|---|
1 | boolean add(E e) Wstawia określony element do tej kolejki, jeśli można to zrobić natychmiast bez naruszenia ograniczeń pojemności, zwraca wartość true po pomyślnym zakończeniu i zgłasza wyjątek IllegalStateException, jeśli obecnie nie ma wolnego miejsca. |
2 | boolean contains(Object o) Zwraca wartość true, jeśli ta kolejka zawiera określony element. |
3 | int drainTo(Collection<? super E> c) Usuwa wszystkie dostępne elementy z tej kolejki i dodaje je do danej kolekcji. |
4 | int drainTo(Collection<? super E> c, int maxElements) Usuwa co najwyżej podaną liczbę dostępnych elementów z tej kolejki i dodaje je do danej kolekcji. |
5 | boolean offer(E e) Wstawia określony element do tej kolejki, jeśli można to zrobić natychmiast bez naruszania ograniczeń pojemności, zwraca wartość true w przypadku sukcesu i false, jeśli obecnie nie ma dostępnego miejsca. |
6 | boolean offer(E e, long timeout, TimeUnit unit) Wstawia określony element do tej kolejki, czekając do określonego czasu oczekiwania, jeśli jest to konieczne, na udostępnienie miejsca. |
7 | E poll(long timeout, TimeUnit unit) Pobiera i usuwa nagłówek tej kolejki, czekając do określonego czasu oczekiwania, jeśli jest to konieczne, na udostępnienie elementu. |
8 | void put(E e) Wstawia określony element do tej kolejki, czekając, jeśli to konieczne, na udostępnienie miejsca. |
9 | int remainingCapacity() Zwraca liczbę dodatkowych elementów, które ta kolejka może idealnie (w przypadku braku pamięci lub ograniczeń zasobów) zaakceptować bez blokowania lub Integer.MAX_VALUE, jeśli nie ma wewnętrznego limitu. |
10 | boolean remove(Object o) Usuwa pojedyncze wystąpienie określonego elementu z tej kolejki, jeśli jest obecne. |
11 | E take() Pobiera i usuwa nagłówek tej kolejki, czekając, jeśli to konieczne, aż element stanie się dostępny. |
Przykład
Poniższy program TestThread pokazuje użycie interfejsu BlockingQueue w środowisku opartym na wątkach.
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class TestThread {
public static void main(final String[] arguments) throws InterruptedException {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(10);
Producer producer = new Producer(queue);
Consumer consumer = new Consumer(queue);
new Thread(producer).start();
new Thread(consumer).start();
Thread.sleep(4000);
}
static class Producer implements Runnable {
private BlockingQueue<Integer> queue;
public Producer(BlockingQueue queue) {
this.queue = queue;
}
@Override
public void run() {
Random random = new Random();
try {
int result = random.nextInt(100);
Thread.sleep(1000);
queue.put(result);
System.out.println("Added: " + result);
result = random.nextInt(100);
Thread.sleep(1000);
queue.put(result);
System.out.println("Added: " + result);
result = random.nextInt(100);
Thread.sleep(1000);
queue.put(result);
System.out.println("Added: " + result);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class Consumer implements Runnable {
private BlockingQueue<Integer> queue;
public Consumer(BlockingQueue queue) {
this.queue = queue;
}
@Override
public void run() {
try {
System.out.println("Removed: " + queue.take());
System.out.println("Removed: " + queue.take());
System.out.println("Removed: " + queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Spowoduje to następujący wynik.
Wynik
Added: 52
Removed: 52
Added: 70
Removed: 70
Added: 27
Removed: 27
Interfejs java.util.concurrent.ConcurrentMap jest podinterfejsem interfejsu Map, obsługuje niepodzielne operacje na bazowej zmiennej mapy. Posiada metody pobierania i ustawiania, które działają jak odczyty i zapisy na zmiennych zmiennych. Oznacza to, że zestaw ma relację zdarzenie przed każdym kolejnym uzyskaniem tej samej zmiennej. Ten interfejs zapewnia bezpieczeństwo wątków i gwarancje atomowości.
Metody ConcurrentMap
Sr.No. | Metoda i opis |
---|---|
1 | default V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) Próbuje obliczyć mapowanie dla określonego klucza i jego bieżącej zamapowanej wartości (lub null, jeśli nie ma bieżącego mapowania). |
2 | default V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) Jeśli określony klucz nie jest już skojarzony z wartością (lub jest zamapowany na null), próbuje obliczyć jego wartość przy użyciu danej funkcji mapowania i wprowadza go do tej mapy, chyba że ma wartość null. |
3 | default V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) Jeśli wartość dla określonego klucza jest obecna i różna od null, próbuje obliczyć nowe mapowanie, biorąc pod uwagę klucz i jego bieżącą zamapowaną wartość. |
4 | default void forEach(BiConsumer<? super K,? super V> action) Wykonuje daną akcję dla każdego wpisu w tej mapie, dopóki wszystkie wpisy nie zostaną przetworzone lub akcja zgłosi wyjątek. |
5 | default V getOrDefault(Object key, V defaultValue) Zwraca wartość, na którą jest mapowany określony klucz, lub defaultValue, jeśli ta mapa nie zawiera mapowania dla klucza. |
6 | default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) Jeśli określony klucz nie jest już powiązany z wartością lub jest powiązany z wartością null, kojarzy go z daną wartością inną niż null. |
7 | V putIfAbsent(K key, V value) Jeśli określony klucz nie jest jeszcze powiązany z wartością, skojarz go z daną wartością. |
8 | boolean remove(Object key, Object value) Usuwa wpis dla klucza tylko wtedy, gdy jest aktualnie mapowany na daną wartość. |
9 | V replace(K key, V value) Zastępuje wpis dla klucza tylko wtedy, gdy jest aktualnie mapowany na jakąś wartość. |
10 | boolean replace(K key, V oldValue, V newValue) Zastępuje wpis dla klucza tylko wtedy, gdy jest aktualnie mapowany na daną wartość. |
11 | default void replaceAll(BiFunction<? super K,? super V,? extends V> function) Zastępuje wartość każdego wpisu wynikiem wywołania danej funkcji na tym wpisie, aż wszystkie wpisy zostaną przetworzone lub funkcja zgłosi wyjątek. |
Przykład
Poniższy program TestThread przedstawia użycie ConcurrentMap vs HashMap.
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class TestThread {
public static void main(final String[] arguments) {
Map<String,String> map = new ConcurrentHashMap<String, String>();
map.put("1", "One");
map.put("2", "Two");
map.put("3", "Three");
map.put("5", "Five");
map.put("6", "Six");
System.out.println("Initial ConcurrentHashMap: " + map);
Iterator<String> iterator = map.keySet().iterator();
try {
while(iterator.hasNext()) {
String key = iterator.next();
if(key.equals("3")) {
map.put("4", "Four");
}
}
} catch(ConcurrentModificationException cme) {
cme.printStackTrace();
}
System.out.println("ConcurrentHashMap after modification: " + map);
map = new HashMap<String, String>();
map.put("1", "One");
map.put("2", "Two");
map.put("3", "Three");
map.put("5", "Five");
map.put("6", "Six");
System.out.println("Initial HashMap: " + map);
iterator = map.keySet().iterator();
try {
while(iterator.hasNext()) {
String key = iterator.next();
if(key.equals("3")) {
map.put("4", "Four");
}
}
System.out.println("HashMap after modification: " + map);
} catch(ConcurrentModificationException cme) {
cme.printStackTrace();
}
}
}
Spowoduje to następujący wynik.
Wynik
Initial ConcurrentHashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
ConcurrentHashMap after modification: {1 = One, 2 = Two, 3 = Three, 4 = Four, 5 = Five, 6 = Six}
Initial HashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(Unknown Source)
at java.util.HashMap$KeyIterator.next(Unknown Source)
at TestThread.main(TestThread.java:48)
Interfejs java.util.concurrent.ConcurrentNavigableMap jest podinterfejsem interfejsu ConcurrentMap i obsługuje operacje NavigableMap, a także rekurencyjnie dla jego nawigowalnych map podrzędnych i przybliżonych dopasowań.
Metody ConcurrentMap
Sr.No. | Metoda i opis |
---|---|
1 | NavigableSet<K> descendingKeySet() Zwraca widok NavigableSet w odwrotnej kolejności kluczy zawartych w tej mapie. |
2 | ConcurrentNavigableMap<K,V> descendingMap() Zwraca widok odwrotnej kolejności mapowań zawartych w tej mapie. |
3 | ConcurrentNavigableMap<K,V> headMap(K toKey) Zwraca widok części tej mapy, której klucze są mniejsze niż toKey. |
4 | ConcurrentNavigableMap<K,V> headMap(K toKey, boolean inclusive) Zwraca widok części tej mapy, której klucze są mniejsze niż (lub równe, jeśli wartość włącznie jest prawdą) toKey. |
5 | NavigableSet<K> keySet() Zwraca widok NavigableSet kluczy zawartych w tej mapie. |
6 | NavigableSet<K> navigableKeySet() Zwraca widok NavigableSet kluczy zawartych w tej mapie. |
7 | ConcurrentNavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) Zwraca widok części tej mapy, której klucze obejmują zakres od fromKey do toKey. |
8 | ConcurrentNavigableMap<K,V> subMap(K fromKey, K toKey) Zwraca widok części tej mapy, której klucze obejmują zakres od odKey włącznie do toKey wyłącznie. |
9 | ConcurrentNavigableMap<K,V> tailMap(K fromKey) Zwraca widok części tej mapy, której klucze są większe lub równe fromKey. |
10 | ConcurrentNavigableMap<K,V> tailMap(K fromKey, boolean inclusive) Zwraca widok części tej mapy, której klucze są większe niż (lub równe, jeśli wartość włącznie jest prawdą) fromKey. |
Przykład
Poniższy program TestThread przedstawia użycie ConcurrentNavigableMap.
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
public class TestThread {
public static void main(final String[] arguments) {
ConcurrentNavigableMap<String,String> map =
new ConcurrentSkipListMap<String, String>();
map.put("1", "One");
map.put("2", "Two");
map.put("3", "Three");
map.put("5", "Five");
map.put("6", "Six");
System.out.println("Initial ConcurrentHashMap: "+map);
System.out.println("HeadMap(\"2\") of ConcurrentHashMap: "+map.headMap("2"));
System.out.println("TailMap(\"2\") of ConcurrentHashMap: "+map.tailMap("2"));
System.out.println(
"SubMap(\"2\", \"4\") of ConcurrentHashMap: "+map.subMap("2","4"));
}
}
Spowoduje to następujący wynik.
Wynik
Initial ConcurrentHashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
HeadMap("2") of ConcurrentHashMap: {1 = One}
TailMap("2") of ConcurrentHashMap: {2 = Two, 3 = Three, 5 = Five, 6 = Six}
SubMap("2", "4") of ConcurrentHashMap: {2 = Two, 3 = Three}