线程等待多个线程

时间:2018-05-06 08:04:09

标签: java multithreading runnable synchronizing

我必须创建一个树篱模拟器。有例如。它的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 个答案:

答案 0 :(得分:1)

关于该队列。您可以使用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,因为它可能会在同一时间增长并被削减,因为这种情况发生在不同的线程中。

答案 1 :(得分:0)

以下代码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实现 请注意,我对线程的体验有限。我希望其他用户评论并提出改进建议。