Не удается синхронизировать потоки в Java (используя семафоры)

#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);
}