Как синхронизировать потоки в Java?

245
13 апреля 2018, 18:27

Здравствуйте, есть задание создать конвейер, где каждый рабочий это 1 поток. Нужно сделать так чтобы потоки обрабатывали предметы. Написал вот такой код, но он очень не стабильный. Выдает правильный результат через некоторое кол-во запусков. Посоветуйте, что можно сделать (sleep нельзя использовать)

public class Worker extends Thread{
private int id;
private static int itemCounter;
private int current = 1;
private static Worker[] workers;
private boolean done = false;
private Worker(String name, int id) {
    setName(name);
    this.id = id;
}
@Override
public void run() {
    try {
        if (id != 0) {
            synchronized (this) {
                wait();
            }
        }
        while (current <= itemCounter) {
            synchronized (this) {

                System.out.println(Thread.currentThread().getName() + " processing " + current + " item");
                current++;
            }
            if (id < (workers.length - 1)) {
                synchronized (workers[id + 1]) {

                    workers[id + 1].notify();
                }
            }
            if (id > 0) {
                synchronized (workers[id - 1]) {

                    workers[id - 1].notify();
                }
            }

            synchronized (this) {

                if (workers.length != 1) {
                    if (id == (workers.length - 1) && workers[id - 1].done) {
                        continue;
                    }
                    if (current > itemCounter) {
                        done = true;
                        break;
                    }
                    wait();
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public static void main(String[] args) {
    int workerCounter = 0;
    if (args.length != 2) {
        System.err.println("Error, use two arguments!");
        throw new Error();
    } else {
        try {
            workerCounter = Integer.parseInt(args[0]);
            itemCounter = Integer.parseInt(args[1]);
            if (workerCounter <= 0 || itemCounter <= 0) {
                throw new Exception();
            }
        } catch (Exception e) {
            System.err.println("Error: Invalid args.");
            System.exit(1);
        }
        System.out.println(workerCounter + " Workers; " + itemCounter + " Items");
        workers = new Worker[workerCounter];
        for (int i = 0; i < workers.length; i++) {
            workers[i] = new Worker("Worker" + (i + 1), i);
        }
        for (int i = 1; i < workers.length; i++) {
            workers[i].start();
        }
        workers[0].start();
    }
}

}

Результаты

Answer 1

Как верно было подмечено выше synchronized (this) в данном случае не имеет смысла, т.к. лок захватывается на экземпляр Worker, который вызвал метод run(). Насколько я понял из задачи, Вам нужно синхронизироваться по полю current.

Но синхронизации в этом случае стоит предпочесть атомики, т.к. они работают гораздо быстрее. В Вашем случае можно использовать AtomicIntegerи его методы: incrementAndGet(), compareAndSet().

READ ALSO
Создание первоначальных данных в БД приложения

Создание первоначальных данных в БД приложения

Нужно каким то образом при первом запуске создавать БД с рецептами блюд, чтобы затем их просто подгружать из БД и всеКак это можно сделать?...

225
Опции cascade в Hibernate

Опции cascade в Hibernate

Существует 7 вариантов опции cascade у HibernateХотелось бы уточнить какого именно поведения ожидать от каждой из них

200
TabLayout - underline невыбранной вкладки android 4.1.1 (16 api)

TabLayout - underline невыбранной вкладки android 4.1.1 (16 api)

Заметил такую проблему на андройде 41

364