【问题标题】:Thread waiting to multiple threads线程等待多个线程
【发布时间】:2018-05-06 08:04:09
【问题描述】:

我必须创建一个对冲模拟器。有例如。它有10个段,每个段都应该有自己的专用线程模拟段的增长(每次我们要计算段是否长大时,我们都应该进行随机测试)。 此外,还应该有一个额外的园丁线程。 因此,Garder 应该在其大小达到 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 我一般都知道同步的基础知识——同步方法/块、锁、等待和通知方法,但这次我完全不知道该怎么做:( 它就像可怕的僵局!当然,我不希望被勺子喂食。任何类型的提示也会非常有帮助。提前感谢您,祝您有美好的一天!

【问题讨论】:

  • 您可以创建一个BlockingQueue,其中树篱一旦长到 8 高以上就会自行插入,园丁从中接受工作订单。但是您必须添加代码才能获得更好的答案。
  • 欢迎来到 SO。 “我的尝试是这样的”你能发minimal reproducible example 来演示吗?
  • 你好大牛,谢谢你的回答。即使我将段添加到队列中,园丁是否仍然无法观察到当时只有一个的增长?我将非常感谢代码澄清。谢谢!我试图尽可能简单地提出我的问题。我没有附加任何代码,因为我只有随机测试工作,我不知道如何解决剩下的任务。我当时需要更一般的帮助。
  • 不,假设高度为 8 的任何段线程将放入阻塞队列“园丁的东西”,当队列中有元素时,园丁将解除阻塞并读取排队并处理/切割段,当队列为空时,园丁将再次阻塞并等待段增长
  • 对于“更一般的帮助”,此站点不正确。也许softwareengineering.stackexchange.com 会更合适。

标签: java multithreading runnable synchronization


【解决方案1】:

关于那个队列。你可以使用ExecutorService。

让对冲成长

所以让我们有一个可以生长和切割的树篱。

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

然后你就有了一个可以让树篱生长的环境。这将模拟对冲部分;每个环境只负责其中一个部分。当对冲大小消失时,它还会通知Consumer&lt;Integer&gt;。

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&lt;Integer&gt;,所以我们可以使用它。

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,因为它可能会“同时”生长并被切割,因为这发生在不同的线程中。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2023-03-11
      • 2015-08-23
      • 1970-01-01
      • 2020-10-04
      • 1970-01-01
      • 1970-01-01
      • 2017-10-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多