Я пытаюсь реализовать шаблон проектирования пула объектов. Я создал структуру данных, как показано ниже
public class ObjectPool {
private final Queue<PooledObject> objects;
private final List<PooledObject> inUseObjects;
public ObjectPool(final int poolSize) throws IllegalArgumentException {
if (poolSize <= 0) {
throw new IllegalArgumentException("Invalid pool size!");
}
objects = new ArrayDeque<PooledObject>(poolSize);
inUseObjects = new ArrayList<PooledObject>(poolSize);
for (int i = 0; i < poolSize; i++) {
objects.add(new PooledObject());
}
}
public synchronized PooledObject getObject() {
PooledObject object;
while ((object = objects.poll()) == null) {
try {
wait();
} catch (final InterruptedException exception) {
System.out.println(exception.getMessage());
}
}
inUseObjects.add(object);
return object;
}
public synchronized void returnObject(final PooledObject object) {
if (object == null) {
return;
}
inUseObjects.remove(object);
objects.add(object);
notifyAll();
}
}
а также работающий клиент
public class Client implements Runnable {
private final ObjectPool pool;
private final int id;
private static int number = 0;
public Client(final ObjectPool pool) {
this.id = ++number;
this.pool = pool;
}
private PooledObject getObjectFromPool() {
PooledObject object = pool.getObject();
System.out.println(this + " gets " + object);
return object;
}
private void returnObjectToPool(final PooledObject object) {
pool.returnObject(object);
System.out.println(this + " returns " + object);
}
@Override
public void run() {
PooledObject object = getObjectFromPool();
try {
Thread.sleep(0);
} catch (final InterruptedException exception) {
System.out.println(exception.getMessage());
}
returnObjectToPool(object);
}
@Override
public String toString() {
return "User #" + id;
}
}
Я пытаюсь запустить этот код следующим образом:
ObjectPool pool = new ObjectPool(2);
ExecutorService es = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
es.execute(new Thread(new Client(pool)));
}
es.shutdown();
try {
if (es.awaitTermination(1, TimeUnit.MINUTES)) {
System.out.println("Object pool pattern implemented!");
}
} catch (InterruptedException exception) {
System.out.println(exception.getMessage());
}
Проблема в том, что синхронизация не работает должным образом.
Насколько я понимаю, назначение ключевого слова synchronized:
Это позволяет синхронизировать другие блоки одного и того же объекта, так что если у вас есть два блока кода, которые могут изменить состояние одного и того же объекта, они не будут мешать друг другу.
Но когда я запускаю программу, из вывода ясно видно, что два потока получают один и тот же объект из пула.
выход:
User #1 gets Object #1
User #2 gets Object #2
User #3 gets Object #2
User #5 gets Object #2
User #4 gets Object #1
User #3 returns Object #2
User #4 returns Object #1
User #2 returns Object #2
User #5 returns Object #2
User #1 returns Object #1
Без запуска этого, чтобы проверить это, это все, что я вижу. Ваш код кажется правильным. Как вы думаете, вы могли бы установить, действительно ли один объект передан более чем одному вызывающему абоненту, не будучи еще освобожденным?
@markspace Я не думал, что проблема может быть в операторе печати. Я также проверю количество звонивших прямо сейчас. Большое спасибо!
@markspace Вы правы, проблема была в операторе печати. Только один поток имеет доступ к объекту из пула. Спасибо!
Рад, что вы разобрались (и узнали кое-что о том, как работает печать)!




... когда я запускаю программу. Из вывода ясно видно, что два потока получают один и тот же объект из пула
Мне кажется, это классическое состояние гонки. Ваши темы Client вызывают getObjectFromPool(), Thread.sleep(0) и returnObjectToPool(...). По сути, sleep(0) является неоперативным, поэтому вполне возможно, что первый поток получит и вернет объект в пул до того, как другой поток вызовет getObjectFromPool(). Кроме того, ваш метод println(...) происходит постфактум, что сбивает с толку упражнение. Обе эти проблемы создают впечатление, что несколько потоков используют один и тот же объект, когда это не так. Кроме того, важно понимать, что System.out — это PrintStream синхронизированный класс, поэтому его использование в многопоточной программе может повлиять на чередование потоков и не рекомендуется.
Если вы хотите проверить, что ваши потоки безопасно получают, обрабатывают и возвращают объекты, вам нужно будет увеличить время ожидания. Кроме того, хотя вы никогда не сделаете этого в пуле производственных объектов, перемещение операторов отладки println(...) внутри методов пула synchronized покажет вам, что происходит с вашим кодом пула потоков.
Наконец, для других есть несколько уже написанных хороших пулов объектов с открытым исходным кодом, включая Apache Commons Pool.
Возможно, операторы печати выполняются не по порядку. Они не синхронизированы. Но я все еще смотрю, вижу ли я какую-либо другую проблему.