Тупик в Java-коде с семафором и приобретением (int) - PullRequest
12 голосов
/ 12 октября 2011

У меня есть следующий код Java:

import java.util.concurrent.*;

class Foo{
    static Semaphore s = new Semaphore(1);

    public void fun(final char c, final int r){
        new Thread(new Runnable(){
            public void run(){
                try{ 
                    s.acquire(r);
                    System.out.println(c+"_"+r);
                    s.release(r+1);
                } catch(Exception e){ e.printStackTrace(); }
            }
        }).start();
    }
}

class ths{
    public static void main(String[]args) throws Exception{
        Foo f = new Foo();
        f.fun('B',2);
        f.fun('F',6);
        f.fun('A',1);
        f.fun('C',3);
        f.fun('D',4);
        f.fun('E',5);
    }
}

В идеале, это должно печатать от A_1 до F_6 в порядке и выходить, но по некоторым причинам этого не происходит. Обычно он печатает A_1 и B_2, а затем застревает.

Я не могу найти что-то явно не так с моим кодом. Есть предложения?

Ответы [ 2 ]

8 голосов
/ 12 октября 2011

Основная проблема в том, что acquire(int permits) не гарантирует, что все разрешения будут получены сразу.Он может получить меньше разрешений, а затем заблокировать в ожидании остальных.

Давайте рассмотрим ваш код.Когда, скажем, три разрешения становятся доступными, ничто не может гарантировать, что они будут переданы потоку C.Фактически их можно было бы передать потоку D для частичного удовлетворения его запроса acquire(4), что привело бы к тупику.

Если вы измените код следующим образом, это решит проблему для меня:

public void fun(final char c, final int r){
    new Thread(new Runnable(){
        public void run(){
            try{ 
                while (!s.tryAcquire(r, 1, TimeUnit.MILLISECONDS)) {};
                System.out.println(c+"_"+r);
                s.release(r+1);
            } catch(Exception e){ e.printStackTrace(); }
        }
    }).start();
}

(Если подумать, вышеупомянутое также нарушено, поскольку нет гарантии, что правильный поток когда-либо получит разрешения - он может продолжать попытки и время ожиданиядо бесконечности.)

0 голосов
/ 13 октября 2011

Semaphore получает сразу все разрешения, иначе это не будет настоящий семафор .НО: версия Java также имеет внутреннюю очередь ожидания.И поведение этой очереди НЕ наилучшим образом соответствует свободным в настоящее время ресурсам , но более или менее разрешений на сбор, пока не будет разрешен запрос первого в очереди .Но перед тем, как поток входит в , в эту очередь выполняется проверка, если доступные разрешения позволяют потоку вообще не заходить в очередь.

Я изменил ваш код, чтобы показать поведение этой очереди:

import java.util.concurrent.*;
public class SemaphoreTest{
    static Semaphore s = new Semaphore(0);

    public void fun(final char c, final int r) throws Exception {
        new Thread(new Runnable(){
            public void run(){
                try{ 
                    System.out.println("acquire "+r);
                    s.acquire(r);
                    System.out.println(c+"_"+r);
                } catch(Exception e){ e.printStackTrace(); }
            }
        }).start();
        Thread.sleep(500);
    }

    public static void main(String[]args) throws Exception{
        SemaphoreTest f = new SemaphoreTest();

        f.fun('B',2);
        f.fun('F',6);
        f.fun('A',1);
        f.fun('C',3);
        f.fun('D',4);
        f.fun('E',5);

        while(s.hasQueuedThreads()){
            Thread.sleep(1000);
            System.out.println("release "+1+", available "+(s.availablePermits()+1));
            s.release(1);
        }
    }
}

В основном были сделаны следующие изменения:

  • Начните с 0 разрешений - пусть кто-нибудь сначала войдет в очередь.
  • "Определите" порядок очередейдавая каждому потоку 500 мс времени после Thread.start.
  • Каждый поток будет вызывать acquire, но не release.
  • Основной поток будет медленно подавать семафор с одним разрешением за другим.

Это даст этот вывод определенно:

acquire 2
acquire 6
acquire 1
acquire 3
acquire 4
acquire 5
release 1, available 1
release 1, available 2
B_2
release 1, available 1
release 1, available 2
release 1, available 3
release 1, available 4
release 1, available 5
release 1, available 6
F_6
release 1, available 1
A_1
release 1, available 1
release 1, available 2
release 1, available 3
C_3
release 1, available 1
release 1, available 2
release 1, available 3
release 1, available 4
D_4
release 1, available 1
release 1, available 2
release 1, available 3
release 1, available 4
release 1, available 5
E_5
release 1, available 1

Что означает: каждый поток пробуждается, если

  • он находится во главеочередь.
  • накоплено достаточно разрешений.
...