#java #multithreading #semaphore
#java #многопоточность #семафор
Вопрос:
Добрый день!
Я столкнулся с проблемой синхронизации потоков. Я пишу программу, которая похожа на ужин философов. У меня мало процессов (например, 3) и ресурсов (например, 4). Каждый процесс может работать только с 2 свободными ресурсами. Это означает, что 1-й процесс может работать только с 1-м и 2-м ресурсами и т.д.
Я решил использовать семафоры для своей цели. Проблема в том, что у is по-прежнему нет синхронизации. Например, если 1-й и 3-й процессы работают с ресурсами, то 2-й процесс должен дождаться, пока его ресурсы не будут освобождены. В моей программе иногда это случается… Иногда это не так.
В чем проблема? Как я могу это решить?
Код здесь:
public class Sem
{
public Sem()
{
available = new ConcurrentHashMap< Integer, Semaphore >();//Resources.
for ( int i = 1; i <= 4; i )
{
available.put( i, new Semaphore(1, true) );//Each resource contains semaphore.
}
}
public void start( final int id )
{
thisThread = new Thread()
{
public void run()
{
try
{
work( id ); //Try to take resourses.
Thread.currentThread().sleep(1000);
release( id ); //Release resources.
} catch (InterruptedException ex) {
Logger.getLogger(Sem.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
thisThread.start();
}
public synchronized void work( int id ) throws InterruptedException
{
available.get(id).acquire(); //Try to take resourse[id] and resourse[id 1]
available.get(id 1).acquire(); //Thread is blocking till it will be possible.
System.out.printf("Acquired [%s], id = %dn",Thread.currentThread().getName(), id);
}
public void release( int id )
{
available.get(id).release(); //Release resources which hava been captured by thread.
available.get(id 1).release(); //After this other thread can take these resourses.
System.out.printf("Released [%s], id = %dn",Thread.currentThread().getName(), id);
}
private ConcurrentHashMap< Integer, Semaphore > available; //Integer - id of thread[1..4]; Semaphore - is gate with param (1)
//Available - map of resources which can be busy by processes.
Thread thisThread;
}
Я запускаю эту программу следующим образом:
Sem sem = new Sem();
sem.start(1);
sem.start(2);
sem.start(3);
У меня мало выходных сообщений, но мое любимое:
Acquired [Thread-1], id = 1
Acquired [Thread-3], id = 3
Released [Thread-1], id = 1
Acquired [Thread-2], id = 2
Released [Thread-3], id = 3
Released [Thread-2], id = 2
Процесс 2 запущен, работал, хотя он не может этого сделать!!
Ответ №1:
Я думаю, вам следует снять блокировки в порядке, обратном тому, в котором они были получены.
Комментарии:
1. Вы имеете в виду использовать next:
available.get(id 1).release(); available.get(id).release();
?2. По крайней мере, это не работает. Я все еще получаю сообщения типа
Acquired [Thread-1], id = 1; Acquired [Thread-3], id = 3; Released [Thread-1], id = 1; Acquired [Thread-2], id = 2; Released [Thread-3], id = 3; Released [Thread-2], id = 2;
Ответ №2:
Основываясь на вашем коде и выходных данных, возможно, что поток 3 выпустил семафор 3, поэтому поток 2, который ожидал этого, получил его и напечатал сообщение до завершения потока 3.
Я заметил, что вы не синхронизировали метод release.
Я настоятельно рекомендую поместить синхронизированный блок как вокруг получения, так и вокруг выпуска.
synchronized(this) {
available.get(id).acquire();
available.get(id 1).acquire();
}
synchronized(this) {
available.get(id).release();
available.get(id 1).release();
}
Комментарии:
1. Если я пытаюсь синхронизировать release, это показывает
Acquired [Thread-1], id = 1; Acquired [Thread-3], id = 3
, а затем ничего не делаю…2. Что, если вместо synchronized (this) вы попробуете synchronized (acquireObject) и synchronized (releaseObject). Поток 2 просто входит в синхронизированный блок и блокируется на семафоре — классическая взаимоблокировка, поскольку другие потоки не могут войти в release.
3. Я решил использовать ReentrantLock () для освобождения (потому что это критический раздел). Теперь, похоже, моя программа работает. Большое вам спасибо!!! Вы действительно помогли мне!
Ответ №3:
Сначала вы заканчиваете работу с ресурсом, а затем с потоком, поэтому сначала освободите семафор ресурса. Замените метод release следующими:
public void release(int id) {
resources.get(id 1).release();
resources.get(id).release();
//resources.get(id 1).release();
System.out.printf("Released [%s], id = %dn", Thread.currentThread().getName(), id);
}