Поток ожидает нескольких потоков

Я должен создать симулятор хеджирования. Есть например. 10 его сегментов, и у каждого из них должен быть свой собственный выделенный поток, имитирующий рост сегмента (каждый раз, когда мы собираемся вычислить, вырос ли сегмент, мы должны выполнить случайный тест). Кроме того, должна быть еще одна нить садовника. Садовник должен отрезать отрезок отсюда, когда его размер достигает 10 (затем он сокращает его размер до исходного уровня 1 и добавляет об этом в свои заметки).

Моя попытка заставить его работать так:

public class Segment implements Runnable {
    private int currentSize;

    @Override
    public void run() {
        if(Math.random() < 0.3)
            incrementSize();
    }

    private synchronized void incrementSize() {
        currentSize++;
    }

    public synchronized int getCurrentSize() {
        return currentSize;
    }

    public synchronized void setCurrentSize(int newSize) {
        currentSize = newSize;
    }
}

public class Gardener implements Runnable {
    private int[] segmentsCutAmount = new int[10]; //Gardener notes
    private Collection<Segment> segments;

    public Gardener(Collection<Segment> segmentsToLookAfter) {
        segments = segmentsToLookAfter;
    }

    @Override
    public void run() {
        while(true) {
            //Have no idea how to deal with 10 different segments here
        }
    }
}

public class Main {
    private Collection<Segment> segments = new ArrayList<>():
    public void main(String[] args) {
        Main program = new Main();
        for(int i = 0; i < 10; i++)
            program.addSegment();

        Thread gardenerThread = new Thread(new Gardener(program.segments));
    }

    private void addSegment(Collection<Segment> segments) {
        Segment segment = new Segment();
        Thread segmentThread = new Thread(segment);
        segmentThread.start();
        segments.add(segment);
    }
}

Я не уверен, что я должен делать, когда сегмент достигает максимальной высоты. Если бы было 10 садоводов, каждый из них мог бы наблюдать один сегмент, но, к сожалению, садовник - одинокий стрелок - у него нет семьи, и его друзья очень заняты и не хотят ему помогать. И вы готовы мне помочь?:D Я, как правило, знаю основы синхронизации - синхронизированные методы / блоки, блокировки, методы ожидания и уведомления, но на этот раз я совершенно не знаю, что делать:(Это похоже на ужасный тупик! Конечно, я не ожидаю, что меня накормят ложкой. Любой вид Намек также будет очень полезен. Спасибо заранее и хорошего дня!

2 ответа

Об этой очереди. Вы можете использовать ExecutorService для этого.

Позволяя Хеджу расти

Итак, у вас есть живая изгородь, которая может расти и обрезаться.

class Hedge {
    private AtomicInteger height = new AtomicInteger(1);
    public int grow() {
        return height.incrementAndGet();
    }
    public int cut() {
        return height.decrementAndGet();
    }
}

И тогда у вас будет среда, которая позволит хеджу расти. Это будет имитировать секции хеджирования; каждая среда отвечает только за один из разделов. Это также уведомит Consumer<Integer> когда размер хеджирования ушел.

class SectionGrower implements Runnable {
    public static final Random RANDOM = new Random();
    private final Hedge hedge;
    private final Consumer<Integer> hedgeSizeListener;
    public SectionGrower (Hedge h, Consumer<Integer> hl) { 
        hedge = h; 
        hedgeSizeListener = hl 
    }
    public void run() {
        while (true) { // grow forever
            try { 
                // growing the hedge takes up to 20 seconds
                Thread.sleep(RANDOM.nextInt(20)*1000);
                int sectionHeight = hedge.grow();
                hedgeSizeListener.accept(sectionHeight);
            } catch (Exception e) {} // do something here
        }
    }
}

Итак, на данный момент, вы можете сделать это.

ExecutorService growingExecutor = Executors.newFixedThreadPool(10);
Consumer<Integer> printer = i -> System.out.printf("hedge section has grown to %d\n", i.intValue());
for (int i = 0; i < 10; i++) {
    Hedge section = new Hedge();
    Environment grower = new SectionGrower(section, printer);
    growingExecutor.submit(grower::run);
}

Это увеличит 10 секций хеджирования и напечатает текущую высоту для каждого по мере их роста.

Добавление садовника

Так что теперь вам нужен садовник, который может срезать живую изгородь.

class Gardener {
    public static final Random RANDOM = new Random();
    public void cutHedge(Hedge h) {
        try { 
            // cutting the hedge takes up to 10 seconds
            Thread.sleep(RANDOM.nextInt(10)*1000);
            h.cut();
        } catch (Exception e) {} // do something here
    }
}

Теперь вам нужна конструкция, чтобы дать ему работу; это где BlockingQueue приходит. Мы уже убедились, Environment может уведомить Consumer<Integer> после того, как раздел вырос, вот что мы можем использовать.

ExecutorService growingExecutor = Executors.newFixedThreadPool(10);
// so this is the queue
ExecutorService gardenerExecutor = Executors.newSingleThreadPool();
Gardener gardener = new Gardener();
for (int i = 0; i < 10; i++) {
    Hedge section = new Hedge();
    Consumer<Integer> cutSectionIfNeeded = i -> {
        if (i > 8) { // size exceeded?
            // have the gardener cut the section, ie adding item to queue
            gardenerExecutor.submit(() -> gardener.cutHedge(section));
        }
    };
    SectionGrower grower = new SectionGrower(section, cutSectionIfNeeded);
    growingExecutor.submit(grower::run);
}

Так что я на самом деле не пробовал это, но это должно работать с некоторыми небольшими изменениями.

Обратите внимание, что я использую AtomicInteger в изгороди, потому что он может расти и обрезаться "одновременно", потому что это происходит в разных потоках.

В следующем коде Gardner ждет Segment чтобы получить произвольное значение 9.
когда Segment попадает в 9, он уведомляет Gardnerи ждет Gardner закончить обрезку:

import java.util.ArrayList;
import java.util.Collection;

public class Gardening {

    public static void main(String[] args) {

        Collection<Segment> segments = new ArrayList<>();
        for(int i = 0; i < 2; i++) {
            addSegment(segments);
        }
        Thread gardenerThread = new Thread(new Gardener(segments));
        gardenerThread.start();
    }

    private static void addSegment(Collection<Segment> segments) {
        Segment segment = new Segment();
        Thread segmentThread = new Thread(segment);
        segmentThread.start();
        segments.add(segment);
    }
}

class Gardener implements Runnable {

    private Collection<Segment> segments;
    private boolean isStop = false; //add stop flag

    public Gardener(Collection<Segment> segmentsToLookAfter) {
        segments = segmentsToLookAfter;
    }

    @Override
    public void run() {
        for (Segment segment : segments) {
            follow(segment);
        }
    }

    private void follow(Segment segment) {

        new Thread(() -> {

            Thread t = new Thread(segment);
            t.start();
            synchronized (segment) {

                while(! isStop) {
                    try {
                        segment.wait(); //wait for segment
                    } catch (InterruptedException ex) { ex.printStackTrace();}

                    System.out.println("Trimming Segment " + segment.getId()+" size: "
                            + segment.getCurrentSize() ); //add size to notes
                    segment.setCurrentSize(0); //trim size
                    segment.notify(); //notify so segment continues
                }
            }

        }).start();
    }
}

class Segment implements Runnable {

    private int currentSize;
    private boolean isStop = false; //add stop flag
    private static int segmentIdCounter = 0;
    private int segmentId = segmentIdCounter++; //add an id to identify thread

    @Override
    public void run() {
        synchronized (this) {
            while ( ! isStop ) {

                if(Math.random() < 0.0000001) {
                    incrementSize();
                }

                if(getCurrentSize() >= 9) {
                    notify(); //notify so trimming starts
                    try {
                        wait(); //wait for gardener to finish
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        }
    }

    private synchronized void incrementSize() {
        currentSize++;
        System.out.println("Segment " + getId()+" size: "
                + getCurrentSize() );
    }

    public synchronized int getCurrentSize() {  return currentSize;  }

    public synchronized void setCurrentSize(int newSize) {
        currentSize = newSize;
    }
    public int getId() { return segmentId; }
}

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

Другие вопросы по тегам