Semaphore на примитивных функциях Java

217
24 февраля 2018, 21:43

Задача - написать семафор без util.concurrent.*, только wait/notify/notifyAll Сам семафор:

public class MySemaphore {
    private int permits;
    public MySemaphore(int permits) {
        this.permits = permits;
    }
    public synchronized void acquire() throws InterruptedException {
        if (permits > 0) {
            permits--;
        }
        else {
            this.wait();
            permits--;
        }
    }
    public synchronized void release() {
        permits++;
        if (permits > 0) {
            this.notify();
        }
    }
    public boolean tryAcquire() {
        if (permits > 0) {
            return true;
        }
        return false;
    }
}

Два типа потоков для проверки:

1) Инкрементный

public class IncrementThread implements Runnable {
    MySemaphore semaphore;
    String name;
    int count;
    public String getName() {
        return name;
    }
    public IncrementThread(MySemaphore semaphore, String name, int count) {
        this.semaphore = semaphore;
        this.name = name;
        this.count = count;
        System.out.println(name + " was created");
    }
    @Override
    public synchronized void run() {
        System.out.println(Thread.currentThread().getName() + " is waiting for permit");
        try {
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + " is got for permit");
            for (int i=0; i<count; i++) {
                SharedValue.count++;
                System.out.println(Thread.currentThread().getName() + " : value = " + SharedValue.count);
                Thread.sleep(2000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " has released permit");
        semaphore.release();
    }
}

2) Дикрементный

public class DecrementThread implements Runnable {
    MySemaphore semaphore;
    String name;
    int count;
    public DecrementThread(MySemaphore semaphore, String name, int count) {
        this.semaphore = semaphore;
        this.name = name;
        this.count = count;
        System.out.println(name + " was created");
    }
    @Override
    public synchronized void run() {
        System.out.println(Thread.currentThread().getName() + " is waiting for permit");
        try {
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + " is got for permit");
            for (int i=0; i<count; i++) {
                SharedValue.count--;
                System.out.println(Thread.currentThread().getName() + " : value = " + SharedValue.count);
                Thread.sleep(2000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " has released permit");
        semaphore.release();
    }
}

Отдельный класс, который собираюсь мучить из разных потоков:

public class SharedValue {
    static volatile int count = 0;
}

И сам тест:

public class MySemaphoreTest {
    public static void main(String[] args) {
        MySemaphore semaphore = new MySemaphore(2);
        System.out.println("Semaphore with 2 permits was created");
        System.out.println("Shared: " + SharedValue.count);
        IncrementThread thread1 = new IncrementThread(semaphore, "First increment thread", 3);
        IncrementThread thread2 = new IncrementThread(semaphore, "Second increment thread", 4);
        DecrementThread thread3 = new DecrementThread(semaphore, "First decrement thread", 7);
        //DecrementThread thread4 = new DecrementThread(semaphore, "Second decrement thread", 5);
        new Thread(thread1, "firstIncrementThread").start();
        new Thread(thread2, "secondIncrementThread").start();
        new Thread(thread3, "firstDecrementThread").start();
        //new Thread(thread4, "secondDecrementThread").start();
    }
}

Как мне кажется (по-крайней мере, так задумывалось), в конце исполнения программы я должен получить SharedValue.count = 0, а получаю какой-то рандом.

В логах видно, что нити после нескольких итераций "выстреливают" результат одновременно, хотя опять-же, по задумке, должны проводить операцию раз в секунду.

Проблема в реализации семафора или в тестировании?

Answer 1

Результат одновременно "выстреливают" не потоки, а логи этих потоков. Сами потоки работают как положено с задержкой в 2 секунды.

Добавьте два synchronized метода incrementCount и decrementCount, и работайте с ними вместо переменной напрямую..

public class SharedValue {
    private static volatile int count = 0;
    public static int getCount() {
        return count;
    }
    public static synchronized void incrementCount() {
        count++;
    }
    public static synchronized void decrementCount() {
        count--;
    }
}
READ ALSO
Как правильно запихнуть canvas в поток?

Как правильно запихнуть canvas в поток?

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

203
Проверка ходов в шахматах

Проверка ходов в шахматах

Пишу код для шахматСтолкнулся с проблемой определения корректности хода EnPassant у пешки, то есть, когда вражеская пешка "прыгает" на две клетки...

158
Получить данные из POST запроса в PlayFramework

Получить данные из POST запроса в PlayFramework

Отправляю post-запросом файл,

204
Как распределить логику между классами UI

Как распределить логику между классами UI

У меня есть класс которые в себе содержит логику RecyclerView, также в этом классе содержится логика меню, а помимо этого еще toolBar и другие компоненты...

159