Я не буду много говорить. Мои уважаемые коллеги уже внесли свой ценный вклад. Полноценный исполняемый код в конце этого блога должен устранить любую путаницу. Речь идет о небольшой программе бронирования мест в кино в многопоточном сценарии.
Некоторые важные элементарные факты заключаются в следующем.
1> Различные потоки могут бороться только за экземплярные и статические переменные-члены в пространстве кучи.
2> Volatile чтение или запись полностью атомарны и сериализуются / происходят раньше и делаются только из памяти. Говоря это, я имею в виду, что любое чтение будет следовать за предыдущей записью в памяти. И любая запись будет следовать за предыдущим чтением из памяти. Таким образом, любой поток, работающий с volatile, всегда будет видеть самое актуальное значение.
AtomicReference использует это свойство volatile.
Ниже приводится часть исходного кода AtomicReference.
AtomicReference ссылается на ссылку на объект. Эта ссылка является переменной типа volatile в экземпляре AtomicReference, как показано ниже.
private volatile V value;
get () просто возвращает последнее значение переменной (как это происходит с волатильным способом «происходит раньше»).
public final V get()
Ниже приводится наиболее важный метод AtomicReference.
public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
Метод compareAndSet (ожидание, обновление) вызывает метод compareAndSwapObject () небезопасного класса Java. Этот вызов метода unsafe вызывает собственный вызов, который вызывает единственную инструкцию для процессора. «ожидайте» и «обновляйте» каждая ссылка на объект.
В том и только в том случае, если переменная-член экземпляра AtomicReference «значение» ссылается на тот же объект, на который ссылается «ожидание», «переменная» теперь назначается этой переменной экземпляра, и возвращается «истина». Или же ложь возвращается. Все это сделано атомарно. Никакой другой поток не может перехватить между ними.
Поскольку это однопроцессорная операция (магия современной компьютерной архитектуры), она часто быстрее, чем использование синхронизированного блока. Но помните, что, когда несколько переменных необходимо обновить атомарно, AtomicReference не поможет.
Я бы хотел добавить полноценный работающий код, который можно запускать в Eclipse. Было бы ясно, много путаницы. Здесь 22 пользователя (темы MyTh) пытаются забронировать 20 мест. Ниже приведен фрагмент кода, за которым следует полный код.
Фрагмент кода, где 22 пользователя пытаются забронировать 20 мест.
for (int i = 0; i < 20; i++) {// 20 seats
seats.add(new AtomicReference<Integer>());
}
Thread[] ths = new Thread[22];// 22 users
for (int i = 0; i < ths.length; i++) {
ths[i] = new MyTh(seats, i);
ths[i].start();
}
Ниже приводится полный рабочий код.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class Solution {
static List<AtomicReference<Integer>> seats;// Movie seats numbered as per
// list index
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
seats = new ArrayList<>();
for (int i = 0; i < 20; i++) {// 20 seats
seats.add(new AtomicReference<Integer>());
}
Thread[] ths = new Thread[22];// 22 users
for (int i = 0; i < ths.length; i++) {
ths[i] = new MyTh(seats, i);
ths[i].start();
}
for (Thread t : ths) {
t.join();
}
for (AtomicReference<Integer> seat : seats) {
System.out.print(" " + seat.get());
}
}
/**
* id is the id of the user
*
* @author sankbane
*
*/
static class MyTh extends Thread {// each thread is a user
static AtomicInteger full = new AtomicInteger(0);
List<AtomicReference<Integer>> l;//seats
int id;//id of the users
int seats;
public MyTh(List<AtomicReference<Integer>> list, int userId) {
l = list;
this.id = userId;
seats = list.size();
}
@Override
public void run() {
boolean reserved = false;
try {
while (!reserved && full.get() < seats) {
Thread.sleep(50);
int r = ThreadLocalRandom.current().nextInt(0, seats);// excludes
// seats
//
AtomicReference<Integer> el = l.get(r);
reserved = el.compareAndSet(null, id);// null means no user
// has reserved this
// seat
if (reserved)
full.getAndIncrement();
}
if (!reserved && full.get() == seats)
System.out.println("user " + id + " did not get a seat");
} catch (InterruptedException ie) {
// log it
}
}
}
}