Я реализовал решение для https://www.programcreek.com/2014/08/leetcode-design-phone-directory-java/, как показано в листинге 1 ниже:
class PhoneDirectory {
private final LinkedList<Integer> q;
private final boolean[] used;
public PhoneDirectory(int maxNumbers) {
q = new LinkedList<>();
used = new boolean[maxNumbers];
for (int i = 0; i < maxNumbers; i++) {
q.offer(i);
}
}
public int get() {
if (!q.isEmpty()) {
int num = q.poll();
used[num] = true;
return num;
}
return -1;
}
public void release(int number) {
if (used[number]) {
used[number] = false;
q.offer(number);
}
}
}
Однако указанное решение является однопоточным и завершится ошибкой, если несколько потоков одновременно получат доступ к объекту PhoneDirectory.
Итак, я реализовал поточно-ориентированную версию в листинге 2 ниже, с немного другой логикой:
public class PhoneDirectory {
private int curr;
private final int[] next;
private final Object lock = new Object();
private final long SLEEP = 2000;
public PhoneDirectory(int size) {
next = new int[size];
curr = 0;
for (int i = 0; i < size; i++) {
next[i] = (i + 1) % size;
}
}
public int get(final long sleepInSeconds) throws InterruptedException {
synchronized (lock) {
final long sleepInMills = sleepInSeconds * 1000;
long startTime = System.currentTimeMillis();
try {
while (next[curr] == -1) {
if (System.currentTimeMillis() - startTime >= sleepInMills) {
return -1;
}
lock.wait(SLEEP);
}
int res = curr;
curr = next[curr];
next[res] = -1;
return res;
} finally {
lock.notify();
}
}
}
public void release(int number) {
synchronized (lock) {
try {
if (next[number] != -1) {
return;
}
next[number] = curr;
curr = number;
} finally {
lock.notify();
}
}
}
}
- Действительно ли листинг 2 действительно поточно-ориентирован? И это правильный способ реализации поточно-безопасного кода?
- Может ли он масштабироваться, например, если есть сотни потоков и миллионы чисел?
- Могу ли я просто заменить LinkedList на LinkedBlockingQueue (влистинг 1) и ожидать, что он будет потокобезопасным? Будет ли это лучше, чем перечисление 2 выше?