Поток ожидает нескольких потоков
Я должен создать симулятор хеджирования. Есть например. 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
,
Обратите внимание, что мой опыт работы с потоками ограничен. Я надеюсь, что другие пользователи будут комментировать и предлагать улучшения.