Параллелизм Java - Краткое руководство
Java - это многопоточный язык программирования, что означает, что мы можем разрабатывать многопоточные программы с использованием Java. Многопоточная программа состоит из двух или более частей, которые могут выполняться одновременно, и каждая часть может обрабатывать разные задачи одновременно, оптимально используя доступные ресурсы, особенно когда ваш компьютер имеет несколько процессоров.
По определению, многозадачность - это когда несколько процессов совместно используют общие ресурсы обработки, такие как ЦП. Многопоточность расширяет идею многозадачности на приложения, где вы можете разделить определенные операции в рамках одного приложения на отдельные потоки. Каждый из потоков может работать параллельно. ОС распределяет время обработки не только между различными приложениями, но и между каждым потоком в приложении.
Многопоточность позволяет писать таким образом, чтобы несколько действий могли выполняться одновременно в одной программе.
Жизненный цикл потока
В своем жизненном цикле поток проходит различные стадии. Например, поток создается, запускается, выполняется и затем умирает. На следующей диаграмме показан полный жизненный цикл потока.
Ниже приведены этапы жизненного цикла -
New- Новый поток начинает свой жизненный цикл в новом состоянии. Он остается в этом состоянии, пока программа не запустит поток. Его также называютborn thread.
Runnable- После запуска вновь созданного потока он становится работоспособным. Считается, что поток в этом состоянии выполняет свою задачу.
Waiting- Иногда поток переходит в состояние ожидания, пока поток ожидает, пока другой поток выполнит задачу. Поток переходит обратно в рабочее состояние только тогда, когда другой поток сигнализирует ожидающему потоку продолжить выполнение.
Timed Waiting- Выполняемый поток может войти в состояние ожидания по времени в течение указанного интервала времени. Поток в этом состоянии переходит обратно в рабочее состояние, когда истекает этот временной интервал или когда происходит событие, которого он ожидает.
Terminated (Dead) - Выполняемый поток входит в состояние завершения, когда он завершает свою задачу или иным образом завершается.
Приоритеты потоков
Каждый поток Java имеет приоритет, который помогает операционной системе определять порядок, в котором потоки планируются.
Приоритеты потоков Java находятся в диапазоне от MIN_PRIORITY (константа 1) до MAX_PRIORITY (константа 10). По умолчанию каждому потоку дается приоритет NORM_PRIORITY (константа 5).
Потоки с более высоким приоритетом более важны для программы, и им следует выделять процессорное время перед потоками с более низким приоритетом. Однако приоритеты потоков не могут гарантировать порядок, в котором выполняются потоки, и очень сильно зависят от платформы.
Создание потока путем реализации исполняемого интерфейса
Если ваш класс предназначен для выполнения как поток, вы можете добиться этого, реализовав Runnableинтерфейс. Вам нужно будет выполнить три основных шага -
Шаг 1
В качестве первого шага вам необходимо реализовать метод run (), предоставляемый Runnableинтерфейс. Этот метод обеспечивает точку входа для потока, и вы поместите в него полную бизнес-логику. Ниже приводится простой синтаксис метода run ():
public void run( )
Шаг 2
На втором этапе вы создадите экземпляр Thread объект, используя следующий конструктор -
Thread(Runnable threadObj, String threadName);
Где threadObj - это экземпляр класса, который реализуетRunnable интерфейс и threadName это имя, данное новому потоку.
Шаг 3
После создания объекта Thread вы можете запустить его, вызвав start()метод, который выполняет вызов метода run (). Ниже приводится простой синтаксис метода start () -
void start();
Example
Вот пример, который создает новый поток и запускает его -
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();
}
}
Это даст следующий результат -
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.
Создайте поток, расширив класс потока
Второй способ создать поток - создать новый класс, расширяющий Threadclass, используя следующие два простых шага. Этот подход обеспечивает большую гибкость в обработке нескольких потоков, созданных с использованием доступных методов в классе Thread.
Шаг 1
Вам нужно будет переопределить run( )метод, доступный в классе Thread. Этот метод обеспечивает точку входа для потока, и вы поместите в него полную бизнес-логику. Ниже приведен простой синтаксис метода run () -
public void run( )
Шаг 2
После создания объекта Thread вы можете запустить его, вызвав start()метод, который выполняет вызов метода run (). Ниже приводится простой синтаксис метода start () -
void start( );
Example
Вот предыдущая программа, переписанная для расширения потока -
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();
}
}
Это даст следующий результат -
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.
В этой главе мы обсудим различные аспекты создания благоприятной среды для Java.
Настройка локальной среды
Если вы все еще хотите настроить свою среду для языка программирования Java, то в этом разделе вы узнаете, как загрузить и установить Java на свой компьютер. Ниже приведены шаги по настройке среды.
Java SE бесплатно доступен по ссылке Загрузить Java . Вы можете скачать версию, соответствующую вашей операционной системе.
Следуйте инструкциям, чтобы загрузить Java и запустить .exeчтобы установить Java на свой компьютер. После того, как вы установили Java на свой компьютер, вам нужно будет установить переменные среды, чтобы они указывали на правильные каталоги установки -
Настройка пути для Windows
Предполагая, что вы установили Java в каталог c: \ Program Files \ java \ jdk -
Щелкните правой кнопкой мыши «Мой компьютер» и выберите «Свойства».
Нажмите кнопку «Переменные среды» на вкладке «Дополнительно».
Теперь измените переменную Path, чтобы она также содержала путь к исполняемому файлу Java. Например, если в настоящее время задан путь «C: \ WINDOWS \ SYSTEM32», измените путь на «C: \ WINDOWS \ SYSTEM32; c: \ Program Files \ java \ jdk \ bin».
Настройка пути для Linux, UNIX, Solaris, FreeBSD
Переменная среды PATH должна указывать на место установки двоичных файлов Java. Обратитесь к документации по вашей оболочке, если у вас возникли проблемы с этим.
Например, если вы используете bash в качестве оболочки, вы должны добавить следующую строку в конец вашего '.bashrc: export PATH = / path / to / java: $ PATH'
Популярные редакторы Java
Для написания программ на Java вам понадобится текстовый редактор. На рынке доступны даже более сложные IDE. Но пока вы можете рассмотреть одно из следующих:
Notepad - На компьютере с Windows вы можете использовать любой простой текстовый редактор, например Блокнот (рекомендуется для этого урока), TextPad.
Netbeans - Java IDE с открытым исходным кодом и бесплатно, которую можно загрузить с https://netbeans.org/index.html.
Eclipse - Java IDE, разработанная сообществом разработчиков открытого исходного кода eclipse, которую можно загрузить с https://www.eclipse.org/.
Core Java обеспечивает полный контроль над многопоточной программой. Вы можете разработать многопоточную программу, которую можно приостановить, возобновить или полностью остановить в зависимости от ваших требований. Существуют различные статические методы, которые можно использовать для объектов потока для управления их поведением. В следующей таблице перечислены эти методы -
Sr.No. | Метод и описание |
---|---|
1 | public void suspend() Этот метод переводит поток в приостановленное состояние и может быть возобновлен с помощью метода resume (). |
2 | public void stop() Этот метод полностью останавливает поток. |
3 | public void resume() Этот метод возобновляет поток, который был приостановлен с помощью метода suspend (). |
4 | public void wait() Заставляет текущий поток ждать, пока другой поток не вызовет notify (). |
5 | public void notify() Пробуждает единственный поток, ожидающий на мониторе этого объекта. |
Имейте в виду, что в последних версиях Java не рекомендуется использовать методы suspend (), resume () и stop (), поэтому вам нужно использовать доступные альтернативы.
пример
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.");
}
}
Вышеупомянутая программа производит следующий вывод -
Вывод
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.
Если вы знаете о межпроцессном взаимодействии, вам будет легко понять межпоточное взаимодействие. Межпотоковая связь важна при разработке приложения, в котором два или более потоков обмениваются некоторой информацией.
Есть три простых метода и небольшой трюк, делающий возможным взаимодействие потоков. Все три метода перечислены ниже -
Sr.No. | Метод и описание |
---|---|
1 | public void wait() Заставляет текущий поток ждать, пока другой поток не вызовет notify (). |
2 | public void notify() Пробуждает единственный поток, ожидающий на мониторе этого объекта. |
3 | public void notifyAll() Пробуждает все потоки, вызвавшие wait () для одного и того же объекта. |
Эти методы были реализованы как finalметоды в Object, поэтому они доступны во всех классах. Все три метода могут быть вызваны только изнутриsynchronized контекст.
пример
Этот пример показывает, как два потока могут взаимодействовать, используя wait() и notify()метод. Вы можете создать сложную систему, используя ту же концепцию.
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);
}
}
Когда вышеуказанная программа выполняется и выполняется, она дает следующий результат:
Вывод
Hi
Hi
How are you ?
I am good, what about you?
I am also doing fine!
Great!
Приведенный выше пример был взят, а затем изменен из [https://stackoverflow.com/questions/2170520/inter-thread-communication-in-java]
Пример многопоточности с синхронизацией
Вот тот же пример, который последовательно выводит значение счетчика, и каждый раз, когда мы его запускаем, он дает тот же результат.
пример
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");
}
}
}
Это дает один и тот же результат каждый раз, когда вы запускаете эту программу -
Вывод
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.
Deadlock описывает ситуацию, когда два или более потока заблокированы навсегда, ожидая друг друга. Взаимоблокировка возникает, когда нескольким потокам нужны одинаковые блокировки, но они получают их в разном порядке. Многопоточная программа Java может оказаться в состоянии взаимоблокировки, посколькуsynchronizedКлючевое слово заставляет выполняющийся поток блокироваться в ожидании блокировки или монитора, связанного с указанным объектом. Вот пример.
пример
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...");
}
}
}
}
}
Когда вы компилируете и выполняете вышеуказанную программу, вы обнаруживаете тупиковую ситуацию, и следующий результат, производимый программой:
Вывод
Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...
Вышеупомянутая программа будет зависать навсегда, потому что ни один из потоков не в состоянии продолжить и не ждет друг друга, чтобы снять блокировку, поэтому вы можете выйти из программы, нажав CTRL + C.
Пример решения тупиковой ситуации
Давайте изменим порядок блокировки и запустим одну и ту же программу, чтобы увидеть, ждут ли оба потока по-прежнему друг друга -
пример
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...");
}
}
}
}
}
Таким образом, простое изменение порядка блокировок предотвращает попадание программы в тупиковую ситуацию и завершается со следующим результатом:
Вывод
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...
Приведенный выше пример просто проясняет концепцию, однако это сложная концепция, и вам следует глубоко погрузиться в нее, прежде чем разрабатывать свои приложения для работы с ситуациями тупика.
Класс ThreadLocal используется для создания локальных переменных потока, которые могут быть прочитаны и записаны только одним потоком. Например, если два потока обращаются к коду, имеющему ссылку на одну и ту же переменную threadLocal, каждый поток не увидит никаких изменений в переменной threadLocal, выполненных другим потоком.
Методы ThreadLocal
Ниже приводится список важных методов, доступных в классе ThreadLocal.
Sr.No. | Метод и описание |
---|---|
1 | public T get() Возвращает значение в копии текущего потока этой локальной переменной потока. |
2 | protected T initialValue() Возвращает «начальное значение» текущего потока для этой локальной переменной потока. |
3 | public void remove() Удаляет значение текущего потока для этой локальной переменной потока. |
4 | public void set(T value) Устанавливает копию текущего потока этой локальной переменной потока в указанное значение. |
пример
Следующая программа TestThread демонстрирует некоторые из этих методов класса ThreadLocal. Здесь мы использовали две переменные-счетчики, одна - обычная переменная, а другая - 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");
}
}
}
Это даст следующий результат.
Вывод
Counter: 1
threadLocalCounter: 0
Counter: 2
threadLocalCounter: 0
Counter: 3
threadLocalCounter: 0
Counter: 4
threadLocalCounter: 0
Вы можете видеть, что значение счетчика увеличивается каждым потоком, но threadLocalCounter остается 0 для каждого потока.
Java.util.concurrent.ThreadLocalRandom - это служебный класс, представленный начиная с jdk 1.7 и далее, и полезен, когда для генерации случайных чисел требуется несколько потоков или ForkJoinTasks. Он повышает производительность и вызывает меньше конфликтов, чем метод Math.random ().
Методы ThreadLocalRandom
Ниже приводится список важных методов, доступных в классе ThreadLocalRandom.
Sr.No. | Метод и описание |
---|---|
1 | public static ThreadLocalRandom current() Возвращает ThreadLocalRandom текущего потока. |
2 | protected int next(int bits) Генерирует следующее псевдослучайное число. |
3 | public double nextDouble(double n) Возвращает псевдослучайное равномерно распределенное двойное значение от 0 (включительно) до указанного значения (исключая). |
4 | public double nextDouble(double least, double bound) Возвращает псевдослучайное, равномерно распределенное значение между заданным наименьшим значением (включительно) и связанным (исключительным). |
5 | public int nextInt(int least, int bound) Возвращает псевдослучайное, равномерно распределенное значение между заданным наименьшим значением (включительно) и связанным (исключительным). |
6 | public long nextLong(long n) Возвращает псевдослучайное, равномерно распределенное значение от 0 (включительно) до указанного значения (исключая). |
7 | public long nextLong(long least, long bound) Возвращает псевдослучайное, равномерно распределенное значение между заданным наименьшим значением (включительно) и связанным (исключительным). |
8 | public void setSeed(long seed) Выдает исключение UnsupportedOperationException. |
пример
Следующая программа TestThread демонстрирует некоторые из этих методов интерфейса Lock. Здесь мы использовали lock (), чтобы получить блокировку, и unlock (), чтобы снять блокировку.
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());
}
}
Это даст следующий результат.
Вывод
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)
Здесь мы использовали классы ThreadLocalRandom и Random для получения случайных чисел.
Интерфейс java.util.concurrent.locks.Lock используется как механизм синхронизации потоков, аналогичный синхронизированным блокам. Новый механизм блокировки более гибкий и предоставляет больше возможностей, чем синхронизированный блок. Основные различия между блокировкой и синхронизированным блоком следующие:
Guarantee of sequence- Синхронизированный блок не дает никаких гарантий последовательности, в которой ожидающему потоку будет предоставлен доступ. Интерфейс блокировки справляется с этим.
No timeout- Синхронизированный блок не имеет опции тайм-аута, если блокировка не предоставлена. Интерфейс блокировки предоставляет такую возможность.
Single method - Синхронизированный блок должен полностью содержаться в одном методе, тогда как методы lock () и unlock () интерфейса блокировки могут вызываться разными методами.
Способы блокировки
Ниже приводится список важных методов, доступных в классе Lock.
Sr.No. | Метод и описание |
---|---|
1 | public void lock() Приобретает замок. |
2 | public void lockInterruptibly() Получает блокировку, если текущий поток не прерывается. |
3 | public Condition newCondition() Возвращает новый экземпляр Condition, связанный с этим экземпляром Lock. |
4 | public boolean tryLock() Получает блокировку, только если она свободна во время вызова. |
5 | public boolean tryLock() Получает блокировку, только если она свободна во время вызова. |
6 | public boolean tryLock(long time, TimeUnit unit) Получает блокировку, если она свободна в течение заданного времени ожидания и текущий поток не был прерван. |
7 | public void unlock() Снимает блокировку. |
пример
Следующая программа TestThread демонстрирует некоторые из этих методов интерфейса Lock. Здесь мы использовали lock (), чтобы получить блокировку, и unlock (), чтобы снять блокировку.
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();
}
}
Это даст следующий результат.
Вывод
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.
Здесь мы используем класс ReentrantLock как реализацию интерфейса Lock. Класс ReentrantLock позволяет потоку заблокировать метод, даже если он уже заблокировал другой метод.
Интерфейс java.util.concurrent.locks.ReadWriteLock позволяет нескольким потокам читать одновременно, но только один поток может писать одновременно.
Read Lock - Если ни один поток не заблокировал ReadWriteLock для записи, тогда несколько потоков могут получить доступ к блокировке чтения.
Write Lock - Если ни один поток не читает или не пишет, то один поток может получить доступ к блокировке записи.
Способы блокировки
Ниже приводится список важных методов, доступных в классе Lock.
Sr.No. | Метод и описание |
---|---|
1 | public Lock readLock() Возвращает блокировку, используемую для чтения. |
2 | public Lock writeLock() Возвращает блокировку, использованную для записи. |
пример
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();
}
}
}
}
Это даст следующий результат.
Вывод
Writer A Time Taken 6 seconds.
Write Lock Present.
Writer B Time Taken 2 seconds.
Reader Time Taken 0 seconds.
Reader: aab
Интерфейс java.util.concurrent.locks.Condition предоставляет потоку возможность приостанавливать свое выполнение до тех пор, пока данное условие не станет истинным. Объект Condition обязательно привязан к Lock и должен быть получен с помощью метода newCondition ().
Методы состояния
Ниже приводится список важных методов, доступных в классе Condition.
Sr.No. | Метод и описание |
---|---|
1 | public void await() Заставляет текущий поток ждать, пока он не получит сигнал или не будет прерван. |
2 | public boolean await(long time, TimeUnit unit) Заставляет текущий поток ждать, пока он не получит сигнал или не будет прерван, или пока не истечет указанное время ожидания. |
3 | public long awaitNanos(long nanosTimeout) Заставляет текущий поток ждать, пока он не получит сигнал или не будет прерван, или пока не истечет указанное время ожидания. |
4 | public long awaitUninterruptibly() Заставляет текущий поток ждать, пока не поступит сигнал. |
5 | public long awaitUntil() Заставляет текущий поток ждать, пока он не получит сигнал или не будет прерван, или пока не истечет указанный крайний срок. |
6 | public void signal() Пробуждает один ожидающий поток. |
7 | public void signalAll() Пробуждает все ожидающие потоки. |
пример
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();
}
}
}
}
Это даст следующий результат.
Вывод
[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
Класс java.util.concurrent.atomic.AtomicInteger предоставляет операции с базовым значением int, которое может быть прочитано и записано атомарно, а также содержит расширенные атомарные операции. AtomicInteger поддерживает атомарные операции с базовой переменной типа int. У него есть методы получения и установки, которые работают как чтение и запись для изменчивых переменных. То есть набор имеет отношение «происходит до» с любым последующим получением той же переменной. Атомарный метод compareAndSet также имеет эти функции согласованности памяти.
Методы AtomicInteger
Ниже приводится список важных методов, доступных в классе AtomicInteger.
Sr.No. | Метод и описание |
---|---|
1 | public int addAndGet(int delta) Атомно добавляет заданное значение к текущему значению. |
2 | public boolean compareAndSet(int expect, int update) Атомарно устанавливает значение для данного обновленного значения, если текущее значение совпадает с ожидаемым. |
3 | public int decrementAndGet() Атомарно уменьшает текущее значение на единицу. |
4 | public double doubleValue() Возвращает значение указанного числа в виде двойного числа. |
5 | public float floatValue() Возвращает значение указанного числа в виде числа с плавающей запятой. |
6 | public int get() Получает текущее значение. |
7 | public int getAndAdd(int delta) Атомарно добавляет данное значение к текущему значению. |
8 | public int getAndDecrement() Атомарно уменьшает текущее значение на единицу. |
9 | public int getAndIncrement() Атомарно увеличивает текущее значение на единицу. |
10 | public int getAndSet(int newValue) Атомарно устанавливает заданное значение и возвращает старое значение. |
11 | public int incrementAndGet() Атомарно увеличивает текущее значение на единицу. |
12 | public int intValue() Возвращает значение указанного числа как целое число. |
13 | public void lazySet(int newValue) В конце концов устанавливается на заданное значение. |
14 | public long longValue() Возвращает значение указанного числа в виде длинного числа. |
15 | public void set(int newValue) Устанавливается на заданное значение. |
16 | public String toString() Возвращает строковое представление текущего значения. |
17 | public boolean weakCompareAndSet(int expect, int update) Атомарно устанавливает значение для данного обновленного значения, если текущее значение совпадает с ожидаемым. |
пример
Следующая программа TestThread показывает небезопасную реализацию счетчика в среде на основе потоков.
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());
}
}
Это может дать следующий результат в зависимости от скорости компьютера и чередования потоков.
Вывод
Final number (should be 1000): 1000
пример
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());
}
}
Это даст следующий результат.
Вывод
Final number (should be 1000): 1000
Класс java.util.concurrent.atomic.AtomicLong предоставляет операции с базовым длинным значением, которое может быть прочитано и записано атомарно, а также содержит расширенные атомарные операции. AtomicLong поддерживает атомарные операции с базовой переменной типа long. У него есть методы получения и установки, которые работают как чтение и запись для изменчивых переменных. То есть набор имеет отношение «происходит до» с любым последующим получением той же переменной. Атомарный метод compareAndSet также имеет эти функции согласованности памяти.
Методы AtomicLong
Ниже приводится список важных методов, доступных в классе AtomicLong.
Sr.No. | Метод и описание |
---|---|
1 | public long addAndGet(long delta) Атомно добавляет заданное значение к текущему значению. |
2 | public boolean compareAndSet(long expect, long update) Атомарно устанавливает значение для данного обновленного значения, если текущее значение совпадает с ожидаемым. |
3 | public long decrementAndGet() Атомарно уменьшает текущее значение на единицу. |
4 | public double doubleValue() Возвращает значение указанного числа в виде двойного числа. |
5 | public float floatValue() Возвращает значение указанного числа в виде числа с плавающей запятой. |
6 | public long get() Получает текущее значение. |
7 | public long getAndAdd(long delta) Атомарно добавляет данное значение к текущему значению. |
8 | public long getAndDecrement() Атомарно уменьшает текущее значение на единицу. |
9 | public long getAndIncrement() Атомарно увеличивает текущее значение на единицу. |
10 | public long getAndSet(long newValue) Атомарно устанавливает заданное значение и возвращает старое значение. |
11 | public long incrementAndGet() Атомарно увеличивает текущее значение на единицу. |
12 | public int intValue() Возвращает значение указанного числа как целое число. |
13 | public void lazySet(long newValue) В конце концов устанавливается на заданное значение. |
14 | public long longValue() Возвращает значение указанного числа в виде длинного числа. |
15 | public void set(long newValue) Устанавливается на заданное значение. |
16 | public String toString() Возвращает строковое представление текущего значения. |
17 | public boolean weakCompareAndSet(long expect, long update) Атомарно устанавливает значение для данного обновленного значения, если текущее значение совпадает с ожидаемым. |
пример
Следующая программа TestThread показывает безопасную реализацию счетчика с использованием AtomicLong в среде на основе потоков.
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());
}
}
Это даст следующий результат.
Вывод
Final number (should be 1000): 1000
Класс java.util.concurrent.atomic.AtomicBoolean предоставляет операции с базовым логическим значением, которое можно читать и записывать атомарно, а также содержит расширенные атомарные операции. AtomicBoolean поддерживает атомарные операции с базовой логической переменной. У него есть методы получения и установки, которые работают как чтение и запись для изменчивых переменных. То есть набор имеет отношение «происходит до» с любым последующим получением той же переменной. Атомарный метод compareAndSet также имеет эти функции согласованности памяти.
Методы AtomicBoolean
Ниже приводится список важных методов, доступных в классе AtomicBoolean.
Sr.No. | Метод и описание |
---|---|
1 | public boolean compareAndSet(boolean expect, boolean update) Атомарно устанавливает значение для данного обновленного значения, если текущее значение == ожидаемое значение. |
2 | public boolean get() Возвращает текущее значение. |
3 | public boolean getAndSet(boolean newValue) Атомарно устанавливает данное значение и возвращает предыдущее значение. |
4 | public void lazySet(boolean newValue) В конце концов устанавливается на заданное значение. |
5 | public void set(boolean newValue) Безоговорочно устанавливается на заданное значение. |
6 | public String toString() Возвращает строковое представление текущего значения. |
7 | public boolean weakCompareAndSet(boolean expect, boolean update) Атомарно устанавливает значение для данного обновленного значения, если текущее значение == ожидаемое значение. |
пример
Следующая программа TestThread показывает использование переменной AtomicBoolean в среде на основе потоков.
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();
}
}
Это даст следующий результат.
Вывод
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!
Класс java.util.concurrent.atomic.AtomicReference предоставляет операции с базовой ссылкой на объект, которые можно читать и записывать атомарно, а также содержит расширенные атомарные операции. AtomicReference поддерживает атомарные операции с базовой переменной ссылки на объект. У него есть методы получения и установки, которые работают как чтение и запись для изменчивых переменных. То есть набор имеет отношение «происходит до» с любым последующим получением той же переменной. Атомарный метод compareAndSet также имеет эти функции согласованности памяти.
Методы AtomicReference
Ниже приводится список важных методов, доступных в классе AtomicReference.
Sr.No. | Метод и описание |
---|---|
1 | public boolean compareAndSet(V expect, V update) Атомарно устанавливает значение для данного обновленного значения, если текущее значение == ожидаемое значение. |
2 | public boolean get() Возвращает текущее значение. |
3 | public boolean getAndSet(V newValue) Атомарно устанавливает данное значение и возвращает предыдущее значение. |
4 | public void lazySet(V newValue) В конце концов устанавливается на заданное значение. |
5 | public void set(V newValue) Безоговорочно устанавливается на заданное значение. |
6 | public String toString() Возвращает строковое представление текущего значения. |
7 | public boolean weakCompareAndSet(V expect, V update) Атомарно устанавливает значение для данного обновленного значения, если текущее значение == ожидаемое значение. |
пример
Следующая программа TestThread показывает использование переменной AtomicReference в среде на основе потоков.
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());
}
}
Это даст следующий результат.
Вывод
Message is: hello
Atomic Reference of Message is: Thread 1
Класс java.util.concurrent.atomic.AtomicIntegerArray предоставляет операции с базовым массивом int, который можно читать и записывать атомарно, а также содержит расширенные атомарные операции. AtomicIntegerArray поддерживает атомарные операции с базовой переменной массива int. У него есть методы получения и установки, которые работают как чтение и запись для изменчивых переменных. То есть набор имеет отношение «происходит до» с любым последующим получением той же переменной. Атомарный метод compareAndSet также имеет эти функции согласованности памяти.
Методы AtomicIntegerArray
Ниже приводится список важных методов, доступных в классе AtomicIntegerArray.
Sr.No. | Метод и описание |
---|---|
1 | public int addAndGet(int i, int delta) Атомно добавляет заданное значение к элементу с индексом i. |
2 | public boolean compareAndSet(int i, int expect, int update) Атомарно устанавливает элемент в позиции i на заданное обновленное значение, если текущее значение == ожидаемое значение. |
3 | public int decrementAndGet(int i) Атомно уменьшает на единицу элемент с индексом i. |
4 | public int get(int i) Получает текущее значение в позиции i. |
5 | public int getAndAdd(int i, int delta) Атомно добавляет заданное значение к элементу с индексом i. |
6 | public int getAndDecrement(int i) Атомно уменьшает на единицу элемент с индексом i. |
7 | public int getAndIncrement(int i) Атомарно увеличивает на единицу элемент с индексом i. |
8 | public int getAndSet(int i, int newValue) Атомно устанавливает элемент в позиции i на заданное значение и возвращает старое значение. |
9 | public int incrementAndGet(int i) Атомарно увеличивает на единицу элемент с индексом i. |
10 | public void lazySet(int i, int newValue) В конце концов устанавливает элемент в позиции i на заданное значение. |
11 | public int length() Возвращает длину массива. |
12 | public void set(int i, int newValue) Устанавливает элемент в позиции i на заданное значение. |
13 | public String toString() Возвращает строковое представление текущих значений массива. |
14 | public boolean weakCompareAndSet(int i, int expect, int update) Атомарно устанавливает элемент в позиции i на заданное обновленное значение, если текущее значение == ожидаемое значение. |
пример
Следующая программа TestThread показывает использование переменной AtomicIntegerArray в среде на основе потоков.
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");
}
}
}
}
}
Это даст следующий результат.
Вывод
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
Класс java.util.concurrent.atomic.AtomicLongArray предоставляет операции с нижележащим длинным массивом, который можно читать и записывать атомарно, а также содержит расширенные атомарные операции. AtomicLongArray поддерживает атомарные операции с базовой переменной длинного массива. У него есть методы получения и установки, которые работают как чтение и запись для изменчивых переменных. То есть набор имеет отношение «происходит до» с любым последующим получением той же переменной. Атомарный метод compareAndSet также имеет эти функции согласованности памяти.
Методы AtomicLongArray
Ниже приводится список важных методов, доступных в классе AtomicLongArray.
Sr.No. | Метод и описание |
---|---|
1 | public long addAndGet(int i, long delta) Атомно добавляет заданное значение к элементу с индексом i. |
2 | public boolean compareAndSet(int i, long expect, long update) Атомарно устанавливает элемент в позиции i на заданное обновленное значение, если текущее значение == ожидаемое значение. |
3 | public long decrementAndGet(int i) Атомно уменьшает на единицу элемент с индексом i. |
4 | public long get(int i) Получает текущее значение в позиции i. |
5 | public long getAndAdd(int i, long delta) Атомно добавляет заданное значение к элементу с индексом i. |
6 | public long getAndDecrement(int i) Атомно уменьшает на единицу элемент с индексом i. |
7 | public long getAndIncrement(int i) Атомарно увеличивает на единицу элемент с индексом i. |
8 | public long getAndSet(int i, long newValue) Атомно устанавливает элемент в позиции i на заданное значение и возвращает старое значение. |
9 | public long incrementAndGet(int i) Атомарно увеличивает на единицу элемент с индексом i. |
10 | public void lazySet(int i, long newValue) В конце концов устанавливает элемент в позиции i на заданное значение. |
11 | public int length() Возвращает длину массива. |
12 | public void set(int i, long newValue) Устанавливает элемент в позиции i на заданное значение. |
13 | public String toString() Возвращает строковое представление текущих значений массива. |
14 | public boolean weakCompareAndSet(int i, long expect, long update) Атомарно устанавливает элемент в позиции i на заданное обновленное значение, если текущее значение == ожидаемое значение. |
пример
Следующая программа TestThread показывает использование переменной AtomicIntegerArray в среде на основе потоков.
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");
}
}
}
}
}
Это даст следующий результат.
Вывод
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
Класс java.util.concurrent.atomic.AtomicReferenceArray предоставляет операции с базовым ссылочным массивом, который можно читать и записывать атомарно, а также содержит расширенные атомарные операции. AtomicReferenceArray поддерживает атомарные операции с базовой переменной ссылочного массива. У него есть методы получения и установки, которые работают как чтение и запись для изменчивых переменных. То есть набор имеет отношение «происходит до» с любым последующим получением той же переменной. Атомарный метод compareAndSet также имеет эти функции согласованности памяти.
Методы AtomicReferenceArray
Ниже приводится список важных методов, доступных в классе AtomicReferenceArray.
Sr.No. | Метод и описание |
---|---|
1 | public boolean compareAndSet(int i, E expect, E update) Атомарно устанавливает элемент в позиции i на заданное обновленное значение, если текущее значение == ожидаемое значение. |
2 | public E get(int i) Получает текущее значение в позиции i. |
3 | public E getAndSet(int i, E newValue) Атомно устанавливает элемент в позиции i на заданное значение и возвращает старое значение. |
4 | public void lazySet(int i, E newValue) В конце концов устанавливает элемент в позиции i на заданное значение. |
5 | public int length() Возвращает длину массива. |
6 | public void set(int i, E newValue) Устанавливает элемент в позиции i на заданное значение. |
7 | public String toString() Возвращает строковое представление текущих значений массива. |
8 | public boolean weakCompareAndSet(int i, E expect, E update) Атомарно устанавливает элемент в позиции i на заданное обновленное значение, если текущее значение == ожидаемое значение. |
пример
Следующая программа TestThread показывает использование переменной AtomicReferenceArray в среде на основе потоков.
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");
}
}
}
}
}
Это даст следующий результат.
Вывод
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
Интерфейс java.util.concurrent.Executor - это простой интерфейс для поддержки запуска новых задач.
Методы ExecutorService
Sr.No. | Метод и описание |
---|---|
1 | void execute(Runnable command) Выполняет заданную команду когда-нибудь в будущем. |
пример
Следующая программа TestThread показывает использование интерфейса Executor в среде на основе потоков.
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();
}
}
}
}
Это даст следующий результат.
Вывод
Running Task!
Task Completed
Интерфейс java.util.concurrent.ExecutorService является подчиненным интерфейсом интерфейса Executor и добавляет функции для управления жизненным циклом как отдельных задач, так и самого исполнителя.
Методы ExecutorService
Sr.No. | Метод и описание |
---|---|
1 | boolean awaitTermination(long timeout, TimeUnit unit) Блокирует до тех пор, пока все задачи не завершат выполнение после запроса на завершение работы, или пока не истечет время ожидания, или пока текущий поток не будет прерван, в зависимости от того, что произойдет раньше. |
2 | <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) Выполняет заданные задачи, возвращая список Futures с их статусом и результатами, когда все выполнено. |
3 | <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) Выполняет заданные задачи, возвращая список Futures с их статусом и результатами, когда все завершено или истечет время ожидания, в зависимости от того, что произойдет раньше. |
4 | <T> T invokeAny(Collection<? extends Callable<T>> tasks) Выполняет заданные задачи, возвращая результат одной из успешно выполненных (т. Е. Без создания исключения), если таковые были. |
5 | <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) Выполняет заданные задачи, возвращая результат одного из успешно завершенных (т. Е. Без создания исключения), если они выполняются до истечения заданного тайм-аута. |
6 | boolean isShutdown() Возвращает истину, если этот исполнитель был выключен. |
7 | boolean isTerminated() Возвращает истину, если все задачи были выполнены после завершения работы. |
8 | void shutdown() Инициирует упорядоченное завершение работы, при котором ранее отправленные задачи выполняются, но новые задачи не принимаются. |
9 | List<Runnable> shutdownNow() Пытается остановить все активно выполняющиеся задачи, останавливает обработку ожидающих задач и возвращает список задач, ожидающих выполнения. |
10 | <T> Future<T> submit(Callable<T> task) Отправляет на выполнение задачу, возвращающую значение, и возвращает Future, представляющий ожидающие результаты задачи. |
11 | Future<?> submit(Runnable task) Отправляет на выполнение задачу Runnable и возвращает Future, представляющую эту задачу. |
12 | <T> Future<T> submit(Runnable task, T result) Отправляет на выполнение задачу Runnable и возвращает Future, представляющую эту задачу. |
пример
Следующая программа TestThread показывает использование интерфейса ExecutorService в среде на основе потоков.
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();
}
}
}
}
Это даст следующий результат.
Вывод
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.ScheduledExecutorService является подинтерфейсом интерфейса ExecutorService и поддерживает будущее и / или периодическое выполнение задач.
ScheduledExecutorService Методы
Sr.No. | Метод и описание |
---|---|
1 | <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) Создает и выполняет ScheduledFuture, который становится включенным после заданной задержки. |
2 | ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) Создает и выполняет одноразовое действие, которое становится доступным после заданной задержки. |
3 | ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) Создает и выполняет периодическое действие, которое активируется сначала после заданной начальной задержки, а затем с заданным периодом; то есть выполнение начнется после initialDelay, затем initialDelay + period, затем initialDelay + 2 * period и так далее. |
4 | ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) Создает и выполняет периодическое действие, которое активируется первым после заданной начальной задержки, а затем с заданной задержкой между завершением одного выполнения и началом следующего. |
пример
Следующая программа TestThread показывает использование интерфейса ScheduledExecutorService в среде на основе потоков.
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");
}
}
}
Это даст следующий результат.
Вывод
beep
beep
beep
beep
Фиксированный пул потоков можно получить, вызвав статический метод newFixedThreadPool () класса Executors.
Синтаксис
ExecutorService fixedPool = Executors.newFixedThreadPool(2);
где
Для обработки задач будут активны не более 2 потоков.
Если отправлено более двух потоков, они удерживаются в очереди до тех пор, пока потоки не станут доступными.
Новый поток создается, чтобы занять его место, если поток завершается из-за сбоя во время выполнения. Завершение работы исполнителя еще не вызвано.
Любой поток существует до выключения пула.
пример
Следующая программа TestThread показывает использование метода newFixedThreadPool в среде на основе потоков.
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();
}
}
}
}
Это даст следующий результат.
Вывод
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
Кэшированный пул потоков можно получить, вызвав статический метод newCachedThreadPool () класса Executors.
Синтаксис
ExecutorService executor = Executors.newCachedThreadPool();
где
newCachedThreadPool создает исполнителя, имеющего расширяемый пул потоков.
Такой исполнитель подходит для приложений, запускающих множество краткосрочных задач.
пример
Следующая программа TestThread показывает использование метода newCachedThreadPool в среде на основе потоков.
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();
}
}
}
}
Это даст следующий результат.
Вывод
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
Запланированный пул потоков можно получить, вызвав статический метод newScheduledThreadPool () класса Executors.
Синтаксис
ExecutorService executor = Executors.newScheduledThreadPool(1);
пример
Следующая программа TestThread показывает использование метода newScheduledThreadPool в среде на основе потоков.
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");
}
}
}
Это даст следующий результат.
Вывод
beep
beep
beep
beep
Пул с одним потоком можно получить, вызвав статический метод newSingleThreadExecutor () класса Executors.
Синтаксис
ExecutorService executor = Executors.newSingleThreadExecutor();
Где метод newSingleThreadExecutor создает исполнителя, который одновременно выполняет одну задачу.
пример
Следующая программа TestThread показывает использование метода newSingleThreadExecutor в среде на основе потоков.
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();
}
}
}
}
Это даст следующий результат.
Вывод
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 - это ExecutorService для выполнения каждой отправленной задачи с использованием одного из, возможно, нескольких объединенных потоков, обычно настраиваемых с использованием заводских методов Executors. Он также предоставляет различные служебные методы для проверки статистики текущих потоков и управления ими.
Методы ThreadPoolExecutor
Sr.No. | Метод и описание |
---|---|
1 | protected void afterExecute(Runnable r, Throwable t) Метод, вызываемый после завершения выполнения данного Runnable. |
2 | void allowCoreThreadTimeOut(boolean value) Устанавливает политику, определяющую, могут ли основные потоки истекать по тайм-ауту и завершаться, если в течение времени поддержки активности не поступают никакие задачи, и заменяются при необходимости при поступлении новых задач. |
3 | boolean allowsCoreThreadTimeOut() Возвращает true, если этот пул позволяет основным потокам истекать по тайм-ауту и завершаться, если задачи не поступают в течение времени keepAlive, заменяя при необходимости при поступлении новых задач. |
4 | boolean awaitTermination(long timeout, TimeUnit unit) Блокирует до тех пор, пока все задачи не завершат выполнение после запроса на завершение работы, или пока не истечет время ожидания, или пока текущий поток не будет прерван, в зависимости от того, что произойдет раньше. |
5 | protected void beforeExecute(Thread t, Runnable r) Метод, вызываемый перед выполнением данного Runnable в данном потоке. |
6 | void execute(Runnable command) Выполняет данную задачу когда-нибудь в будущем. |
7 | protected void finalize() Вызывает завершение работы, когда на этот исполнитель больше нет ссылки и у него нет потоков. |
8 | int getActiveCount() Возвращает приблизительное количество потоков, активно выполняющих задачи. |
9 | long getCompletedTaskCount() Возвращает приблизительное общее количество завершенных задач. |
10 | int getCorePoolSize() Возвращает основное количество потоков. |
11 | long getKeepAliveTime(TimeUnit unit) Возвращает время поддержания активности потока, то есть время, в течение которого потоки, размер которых превышает размер основного пула, могут оставаться в режиме ожидания перед завершением. |
12 | int getLargestPoolSize() Возвращает наибольшее количество потоков, которые когда-либо одновременно находились в пуле. |
13 | int getMaximumPoolSize() Возвращает максимально допустимое количество потоков. |
14 | int getPoolSize() Возвращает текущее количество потоков в пуле. |
15 | BlockingQueue
Возвращает очередь задач, используемую этим исполнителем. |
15 | RejectedExecutionHandler getRejectedExecutionHandler() Возвращает текущий обработчик невыполнимых задач. |
16 | long getTaskCount() Возвращает приблизительное общее количество задач, которые когда-либо были запланированы для выполнения. |
17 | ThreadFactory getThreadFactory() Возвращает фабрику потоков, используемую для создания новых потоков. |
18 | boolean isShutdown() Возвращает истину, если этот исполнитель был выключен. |
19 | boolean isTerminated() Возвращает истину, если все задачи были выполнены после завершения работы. |
20 | boolean isTerminating() Возвращает истину, если этот исполнитель находится в процессе завершения после shutdown () или shutdownNow (), но не завершился полностью. |
21 год | int prestartAllCoreThreads() Запускает все основные потоки, заставляя их бездействовать в ожидании работы. |
22 | boolean prestartCoreThread() Запускает основной поток, заставляя его бездействовать в ожидании работы. |
23 | void purge() Пытается удалить из рабочей очереди все будущие задачи, которые были отменены. |
24 | boolean remove(Runnable task) Удаляет эту задачу из внутренней очереди исполнителя, если она присутствует, в результате чего она не запускается, если она еще не запущена. |
25 | void setCorePoolSize(int corePoolSize) Устанавливает основное количество потоков. |
26 | void setKeepAliveTime(long time, TimeUnit unit) Устанавливает предел времени, в течение которого потоки могут оставаться в режиме ожидания до завершения. |
27 | void setMaximumPoolSize(int maximumPoolSize) Устанавливает максимально допустимое количество потоков. |
28 | void setRejectedExecutionHandler(RejectedExecutionHandler handler) Устанавливает новый обработчик невыполнимых задач. |
29 | void setThreadFactory(ThreadFactory threadFactory) Устанавливает фабрику потоков, используемую для создания новых потоков. |
30 | void shutdown() Инициирует упорядоченное завершение работы, при котором ранее отправленные задачи выполняются, но новые задачи не принимаются. |
31 год | List<Runnable> shutdownNow() Пытается остановить все активно выполняющиеся задачи, останавливает обработку ожидающих задач и возвращает список задач, ожидающих выполнения. |
32 | protected void terminated() Метод вызывается, когда Исполнитель завершает работу. |
33 | String toString() Возвращает строку, идентифицирующую этот пул, а также его состояние, включая индикацию состояния выполнения и предполагаемое количество рабочих и задач. |
пример
Следующая программа TestThread показывает использование интерфейса ThreadPoolExecutor в среде на основе потоков.
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();
}
}
}
}
Это даст следующий результат.
Вывод
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 является подклассом ThreadPoolExecutor и может дополнительно планировать выполнение команд после заданной задержки или периодическое выполнение.
ScheduledThreadPoolExecutor Методы
Sr.No. | Метод и описание |
---|---|
1 | protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task) Изменяет или заменяет задачу, используемую для выполнения вызываемого объекта. |
2 | protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) Изменяет или заменяет задачу, используемую для выполнения исполняемого файла. |
3 | void execute(Runnable command) Выполняет команду с нулевой необходимой задержкой. |
4 | boolean getContinueExistingPeriodicTasksAfterShutdownPolicy() Получает политику о том, следует ли продолжать выполнение существующих периодических задач, даже если этот исполнитель был отключен. |
5 | boolean getExecuteExistingDelayedTasksAfterShutdownPolicy() Получает политику о том, следует ли выполнять существующие отложенные задачи, даже если этот исполнитель был отключен. |
6 | BlockingQueue<Runnable> getQueue() Возвращает очередь задач, используемую этим исполнителем. |
7 | boolean getRemoveOnCancelPolicy() Получает политику, определяющую, должны ли отмененные задачи немедленно удаляться из рабочей очереди во время отмены. |
8 | <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) Создает и выполняет ScheduledFuture, который становится включенным после заданной задержки. |
9 | ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) Создает и выполняет одноразовое действие, которое становится доступным после заданной задержки. |
10 | ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) Создает и выполняет периодическое действие, которое активируется сначала после заданной начальной задержки, а затем с заданным периодом; то есть выполнение начнется после initialDelay, затем initialDelay + period, затем initialDelay + 2 * period и так далее. |
11 | ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) Создает и выполняет периодическое действие, которое активируется первым после заданной начальной задержки, а затем с заданной задержкой между завершением одного выполнения и началом следующего. |
12 | void setContinueExistingPeriodicTasksAfterShutdownPolicy (boolean value) Устанавливает политику в отношении продолжения выполнения существующих периодических задач, даже если этот исполнитель был выключен. |
13 | void setExecuteExistingDelayedTasksAfterShutdownPolicy (boolean value) Устанавливает политику в отношении выполнения существующих отложенных задач, даже если этот исполнитель был отключен. |
14 | void setRemoveOnCancelPolicy(boolean value) Устанавливает политику того, должны ли отмененные задачи немедленно удаляться из рабочей очереди во время отмены. |
15 | void shutdown() Инициирует упорядоченное завершение работы, при котором ранее отправленные задачи выполняются, но новые задачи не принимаются. |
16 | List<Runnable> shutdownNow() Пытается остановить все активно выполняющиеся задачи, останавливает обработку ожидающих задач и возвращает список задач, ожидающих выполнения. |
17 | <T> Future<T> submit(Callable<T> task) Отправляет на выполнение задачу, возвращающую значение, и возвращает Future, представляющий ожидающие результаты задачи. |
18 | Future<?> submit(Runnable task) Отправляет на выполнение задачу Runnable и возвращает Future, представляющую эту задачу. |
19 | <T> Future<T> submit(Runnable task, T result) Отправляет на выполнение задачу Runnable и возвращает Future, представляющую эту задачу. |
пример
Следующая программа TestThread показывает использование интерфейса ScheduledThreadPoolExecutor в среде на основе потоков.
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");
}
}
}
Это даст следующий результат.
Вывод
beep
beep
beep
beep
Объект java.util.concurrent.Callable может возвращать вычисленный результат, выполненный потоком, в отличие от исполняемого интерфейса, который может только запускать поток. Объект Callable возвращает объект Future, который предоставляет методы для отслеживания прогресса задачи, выполняемой потоком. Будущий объект можно использовать для проверки состояния вызываемого объекта и последующего получения результата из вызываемого объекта после завершения потока. Он также обеспечивает функцию тайм-аута.
Синтаксис
//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();
пример
Следующая программа TestThread показывает использование Futures и Callables в среде на основе потоков.
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;
}
}
}
Это даст следующий результат.
Вывод
Factorial Service called for 10!
Factorial Service called for 20!
10! = 3628800
20! = 2432902008176640000
Фреймворк fork-join позволяет разбить определенную задачу на нескольких воркеров, а затем дождаться результата, чтобы объединить их. Он в значительной степени использует возможности многопроцессорной машины. Ниже приведены основные концепции и объекты, используемые в структуре fork-join.
Вилка
Форк - это процесс, в котором задача разбивается на более мелкие и независимые подзадачи, которые могут выполняться одновременно.
Синтаксис
Sum left = new Sum(array, low, mid);
left.fork();
Здесь Sum является подклассом RecursiveTask, а left.fork () разбивает задачу на подзадачи.
Присоединиться
Присоединение - это процесс, в котором задача объединяет все результаты подзадач после того, как подзадачи завершили выполнение, в противном случае она продолжает ждать.
Синтаксис
left.join();
Слева - объект класса Sum.
ForkJoinPool
это специальный пул потоков, предназначенный для работы с разделением задач fork-and-join.
Синтаксис
ForkJoinPool forkJoinPool = new ForkJoinPool(4);
Вот новый ForkJoinPool с уровнем параллелизма 4 CPU.
Рекурсивное действие
RecursiveAction представляет собой задачу, которая не возвращает никакого значения.
Синтаксис
class Writer extends RecursiveAction {
@Override
protected void compute() { }
}
RecursiveTask
RecursiveTask представляет задачу, которая возвращает значение.
Синтаксис
class Sum extends RecursiveTask<Long> {
@Override
protected Long compute() { return null; }
}
пример
Следующая программа TestThread показывает использование инфраструктуры Fork-Join в среде на основе потоков.
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;
}
}
}
}
Это даст следующий результат.
Вывод
32
499500
Интерфейс java.util.concurrent.BlockingQueue является подинтерфейсом интерфейса Queue и дополнительно поддерживает такие операции, как ожидание, когда очередь станет непустой перед извлечением элемента, и ожидание, пока не станет доступным пространство в очереди перед сохранением элемента. .
BlockingQueue методы
Sr.No. | Метод и описание |
---|---|
1 | boolean add(E e) Вставляет указанный элемент в эту очередь, если это возможно сделать немедленно, без нарушения ограничений емкости, возвращая true в случае успеха и генерируя исключение IllegalStateException, если в настоящее время нет свободного места. |
2 | boolean contains(Object o) Возвращает истину, если эта очередь содержит указанный элемент. |
3 | int drainTo(Collection<? super E> c) Удаляет все доступные элементы из этой очереди и добавляет их в данную коллекцию. |
4 | int drainTo(Collection<? super E> c, int maxElements) Удаляет не более указанного количества доступных элементов из этой очереди и добавляет их в данную коллекцию. |
5 | boolean offer(E e) Вставляет указанный элемент в эту очередь, если это возможно сделать немедленно, без нарушения ограничений емкости, возвращая истину в случае успеха и ложь, если в данный момент нет свободного места. |
6 | boolean offer(E e, long timeout, TimeUnit unit) Вставляет указанный элемент в эту очередь, ожидая до указанного времени ожидания, если необходимо, чтобы пространство стало доступным. |
7 | E poll(long timeout, TimeUnit unit) Извлекает и удаляет заголовок этой очереди, ожидая до указанного времени ожидания, если необходимо, чтобы элемент стал доступным. |
8 | void put(E e) Вставляет указанный элемент в эту очередь, ожидая, если необходимо, освободится место. |
9 | int remainingCapacity() Возвращает количество дополнительных элементов, которые эта очередь может идеально (при отсутствии ограничений памяти или ресурсов) принять без блокировки, или Integer.MAX_VALUE, если нет внутреннего ограничения. |
10 | boolean remove(Object o) Удаляет единственный экземпляр указанного элемента из этой очереди, если он присутствует. |
11 | E take() Извлекает и удаляет заголовок этой очереди, при необходимости ожидая, пока элемент не станет доступным. |
пример
Следующая программа TestThread показывает использование интерфейса BlockingQueue в среде на основе потоков.
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();
}
}
}
}
Это даст следующий результат.
Вывод
Added: 52
Removed: 52
Added: 70
Removed: 70
Added: 27
Removed: 27
Интерфейс java.util.concurrent.ConcurrentMap - это подинтерфейс интерфейса Map, поддерживающий атомарные операции с базовой переменной карты. У него есть методы получения и установки, которые работают как чтение и запись для изменчивых переменных. То есть набор имеет отношение «происходит до» с любым последующим получением той же переменной. Этот интерфейс обеспечивает безопасность потоков и гарантии атомарности.
Методы ConcurrentMap
Sr.No. | Метод и описание |
---|---|
1 | default V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) Пытается вычислить сопоставление для указанного ключа и его текущего сопоставленного значения (или null, если текущего сопоставления нет). |
2 | default V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) Если указанный ключ еще не связан со значением (или сопоставлен с нулевым значением), пытается вычислить его значение с помощью заданной функции сопоставления и вводит его в эту сопоставление, если только значение не равно null. |
3 | default V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) Если значение для указанного ключа присутствует и не равно нулю, пытается вычислить новое сопоставление с учетом ключа и его текущего сопоставленного значения. |
4 | default void forEach(BiConsumer<? super K,? super V> action) Выполняет указанное действие для каждой записи на этой карте, пока все записи не будут обработаны или действие не вызовет исключение. |
5 | default V getOrDefault(Object key, V defaultValue) Возвращает значение, которому сопоставлен указанный ключ, или defaultValue, если эта карта не содержит сопоставления для ключа. |
6 | default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) Если указанный ключ еще не связан со значением или связан с null, связывает его с заданным ненулевым значением. |
7 | V putIfAbsent(K key, V value) Если указанный ключ еще не связан со значением, свяжите его с данным значением. |
8 | boolean remove(Object key, Object value) Удаляет запись для ключа, только если в настоящее время сопоставлено с заданным значением. |
9 | V replace(K key, V value) Заменяет запись для ключа, только если в настоящее время сопоставлено с некоторым значением. |
10 | boolean replace(K key, V oldValue, V newValue) Заменяет запись для ключа, только если в настоящее время сопоставлено с заданным значением. |
11 | default void replaceAll(BiFunction<? super K,? super V,? extends V> function) Заменяет значение каждой записи результатом вызова данной функции для этой записи до тех пор, пока все записи не будут обработаны или функция не вызовет исключение. |
пример
Следующая программа TestThread показывает использование ConcurrentMap и 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();
}
}
}
Это даст следующий результат.
Вывод
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)
Интерфейс java.util.concurrent.ConcurrentNavigableMap является подинтерфейсом интерфейса ConcurrentMap и поддерживает операции NavigableMap, а также рекурсивно для его под-карт с возможностью навигации и приблизительных совпадений.
Методы ConcurrentMap
Sr.No. | Метод и описание |
---|---|
1 | NavigableSet<K> descendingKeySet() Возвращает в обратном порядке представление NavigableSet для ключей, содержащихся на этой карте. |
2 | ConcurrentNavigableMap<K,V> descendingMap() Возвращает отображение отображений, содержащихся на этой карте, в обратном порядке. |
3 | ConcurrentNavigableMap<K,V> headMap(K toKey) Возвращает представление части этой карты, ключи которой строго меньше, чем toKey. |
4 | ConcurrentNavigableMap<K,V> headMap(K toKey, boolean inclusive) Возвращает представление части этой карты, ключи которой меньше (или равны, если включено, истинно) toKey. |
5 | NavigableSet<K> keySet() Возвращает представление NavigableSet ключей, содержащихся на этой карте. |
6 | NavigableSet<K> navigableKeySet() Возвращает представление NavigableSet ключей, содержащихся на этой карте. |
7 | ConcurrentNavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) Возвращает представление части этой карты, ключи которой находятся в диапазоне от fromKey до toKey. |
8 | ConcurrentNavigableMap<K,V> subMap(K fromKey, K toKey) Возвращает представление части этой карты, ключи которой находятся в диапазоне от fromKey включительно до toKey, исключая. |
9 | ConcurrentNavigableMap<K,V> tailMap(K fromKey) Возвращает представление части этой карты, ключи которой больше или равны fromKey. |
10 | ConcurrentNavigableMap<K,V> tailMap(K fromKey, boolean inclusive) Возвращает представление части этой карты, ключи которой больше (или равны, если включено, истинно) fromKey. |
пример
Следующая программа TestThread показывает использование 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"));
}
}
Это даст следующий результат.
Вывод
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}