Запрос справки по синхронизации Java - PullRequest
0 голосов
/ 08 апреля 2011

Я реализую диспетчер сетевых потоков для моего приложения.Я создал тест JUnit, который быстро запрашивает и освобождает индекс сетевого потока, вызывая следующие два метода:

protected static final List <Integer> currThreads          = new ArrayList <Integer>();
protected static int                  maxThreads           = 5;
protected static int                  lastGrantedId        = 0;

public static synchronized int reqNewThread(){
    if (currThreads.size() >= maxThreads) return -1;
    ++lastGrantedId;
    currThreads.add(lastGrantedId);
    return lastGrantedId;
}

public static void threadFinished(final int threadId) throws InternalError{
    if (threadId == -1) return;
    synchronized (currThreads) {
        boolean works = currThreads.remove(Integer.valueOf(threadId));
        assert works : ("threadId: " + threadId);
    }
}

После того, как поток завершит свою работу, currThreads не пуст, но reqNewThread иthreadFinished имеют одинаковое количество вызовов, а remove() всегда дает true.Если я синхронизирую весь метод threadFinished, он работает нормально.Вопрос - почему?Единственная используемая глобальная переменная уже синхронизирована, не так ли?

Код тестирования JUnit4:

final int iters = 15;
final Runnable getAndFree = new GetAndFree(iters);
final int sz = 15;
final Thread[] t = new Thread[sz];
for (int i = 0; i < sz; i++)
    t[i] = new Thread(getAndFree);
for (int i = 0; i < sz; i++)
    t[i].start();
for (int i = 0; i < sz; i++)
    t[i].join();
assertEquals(0, currThreads.size());

Источник потока тестера:

private class GetAndFree implements Runnable {
    int iters;

    public GetAndFree(int iters){
        this.iters = iters;
    }

    @Override
    public void run(){
        try {
            int id = -1;
            for (int i = 0; i < iters; i++) {
                while ((id = reqNewThread()) == -1) {
                    Thread.sleep(25);
                };
                System.out.println("Strarted: " + id);
                Thread.sleep((long)(Math.random() * 10));
                threadFinished(id);
                System.out.println("Finished: " + id);
            } // for
        } catch(final Exception ex) {
            ex.printStackTrace();
        }
    }
}

1 Ответ

3 голосов
/ 08 апреля 2011

Единственная используемая глобальная переменная уже синхронизирована, не так ли?

Нет.

Первый метод - синхронизация на объекте Class длякласс, содержащий метод.

Второй метод выполняет синхронизацию на объекте currThreads.

Измените первый метод на следующий, и он должен устранить проблему синхронизации в отношении этих двух методов.

public static int reqNewThread(){
    synchronize(currThread) {
        if (currThreads.size() >= maxThreads) return -1;
        ++lastGrantedId;
        currThreads.add(lastGrantedId);
        return lastGrantedId;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...