Java |如何使其中一个线程在其他线程仍在运行时进入睡眠状态?

问题描述 投票:0回答:1

我目前正在开发一个使用绘图功能为JPanel中的“反弹图像”制作动画的应用程序。为此,我必须学习使用线程。当我在代码中使用它们时,有人建议不要直接使用线程,而可以使用Executor框架和ExecutorService之类的东西。

现在,我的问题是,添加新图像时,我需要确保不会在彼此内部创建它们。当程序检测到它们将相交时,它应等待一段时间,而所有其他线程仍在运行,因此图像仍会在当前位置移动和绘制。但是,发生的是,当我让一个线程进入睡眠状态以等待某个位置为空时,整个程序似乎冻结了。似乎正在运行的唯一东西是图像移动功能。

Code in a Github Gist

这里是代码:

这是BouncingImages类


/* NOTE: requires MyImage.java */

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


import javax.swing.*;


public class BouncingImages extends JFrame implements ActionListener {

    public static void main(String[] args) {
        new BouncingImages();
    }

    static boolean imagesLoaded = true;
    JPanel resetPanel   = new JPanel();
    JPanel runningPanel = new JPanel();
    JPanel pausedPanel  = new JPanel();
    JPanel btnPanel     = new JPanel();
    public static AnimationPanel animationPanel;
    ArrayList <MyImage> imageList = new ArrayList <MyImage>();
    private volatile boolean stopRequested = false; //maybe should use AtomicBoolean
    boolean isRunning = false;
    boolean isReset = true;
    ExecutorService service = Executors.newCachedThreadPool();
    Future f;

    //here is the part of the code responsible for creating a JFrame
    BouncingImages() {

        //set up button panel
        JButton btnStart  = new JButton("Start");
        JButton btnResume = new JButton("Resume");
        JButton btnAdd    = new JButton("Add");
        JButton btnAdd10  = new JButton("Add 10");
        JButton btnStop   = new JButton("Stop");
        JButton btnReset  = new JButton("Reset");
        JButton btnExit   = new JButton("Exit");

        btnStart.addActionListener(this);
        btnResume.addActionListener(this);
        btnAdd.addActionListener(this);
        btnAdd10.addActionListener(this);
        btnStop.addActionListener(this);
        btnReset.addActionListener(this);
        btnExit.addActionListener(this);

        resetPanel.add(btnStart);

        runningPanel.add(btnAdd);
        runningPanel.add(btnAdd10);
        runningPanel.add(btnStop);

        pausedPanel.add(btnResume);
        pausedPanel.add(btnReset);

        animationPanel = new AnimationPanel();

        resetButtons();
        this.add(btnPanel, BorderLayout.SOUTH);
        this.add(animationPanel);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.pack(); //since the JPanel is controlling the size, we need pack() here.
        this.setLocationRelativeTo(null);    //after pack();
        this.setVisible(true);
    }

    //I use different JPanels with designated buttons to make them display different buttons when the program is running, paused or completely restarted.
    public void resetButtons() {
        btnPanel.updateUI();
        if (isReset) {
            btnPanel.removeAll();
            btnPanel.add(resetPanel, BorderLayout.SOUTH);
        } else {
            if (isRunning) {
                btnPanel.removeAll();
                btnPanel.add(runningPanel, BorderLayout.SOUTH);
            }
            if (!isRunning) {
                btnPanel.removeAll();
                btnPanel.add(pausedPanel, BorderLayout.SOUTH);
            }
        }
    }

    //ActionListener for Buttons
    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand().equals("Start")) {
            startAnimation();
            isRunning = true;
            isReset = false;
            resetButtons();
        }
        if (e.getActionCommand().equals("Resume")) {
            startAnimation();
            isRunning = true;
            resetButtons();
        }
        if (e.getActionCommand().equals("Add")) {
            addImage();

        }
        if (e.getActionCommand().equals("Add 10")) {
            for(int i = 0; i <10; i++) {
                addImage();
                startAnimation();
            }

        }
        if (e.getActionCommand().equals("Stop")) {
            pauseAnimation();
            isRunning = false;
            resetButtons();
        }
        if (e.getActionCommand().equals("Reset")) {
            imageList.clear();
            repaint();
            isReset = true;
            resetButtons();
        }
        if (e.getActionCommand().equals("Exit")) {
            System.exit(0);
        }

    }
    //This function starts all the animations using the Runnable AnimationThread
    void startAnimation() {
        //this starts the program
        if (f == null) {
            f = service.submit(new AnimationThread());
        }
        //this starts the program after it got paused
        else if (f.isCancelled()) {
            f = service.submit(new AnimationThread());
        }
    }
    //this pauses all the animations
    void pauseAnimation() {
        f.cancel(true);
    }

    //here is the part of the code that I have problems with. I'm not sure how to make the program wait for a spot to be empty while all the other threads are running rather than pausing them all.
    void addImage(){
        int i = 0;
        MyImage image;
        while (true) {
            image= new MyImage("image.png");
            if (checkCollision(image)) {
                if(i > 100){
                    System.out.println("Something went wrong");
                    System.exit(0);
                }
                try {
                    i++;
                    Thread.sleep(50);
                } catch (InterruptedException e) {}
            } else {
                System.out.println("image added");
                break;
            }
        }

        imageList.add(image);
    }

    //this part of the program does all the image moving. It uses the function move image from the MyImage class and a checkCollision function from this class.
    void moveAllImages() {
        for (MyImage image : imageList) {
            image.moveImage(animationPanel);
            image.calculatePoints();
            checkCollision(image);
        }
    }

    //this checks all the collisions with other images. It can be used for checking if a image can be created in some spot and also for all the bouncing callculations
    boolean checkCollision(MyImage currentImage){

        if(imageList.isEmpty()) return false;
        for(MyImage im : imageList){
            if(currentImage == im) continue;

            if(currentImage.intersects(im)){
                if(im.contains(currentImage.ml) || im.contains(currentImage.mr)){
                    currentImage.undoMove();
                    currentImage.vx = -currentImage.vx;
                    return true;
                }
            }

            if(im.contains(currentImage.mt) || im.contains(currentImage.mb)){
                currentImage.undoMove();
                currentImage.vy = - currentImage.vy;
                return true;
            }

            if(currentImage.contains(im.ml) || currentImage.contains(im.mr)){
                currentImage.undoMove();
                currentImage.vx = -currentImage.vx;
                return true;
            }

            if (im.contains(currentImage.tl) || im.contains(currentImage.tr) || im.contains(currentImage.bl) || im.contains(currentImage.br)) {
                currentImage.undoMove();
                currentImage.vx *= -1;
                currentImage.vy *= -1;
                return true;
            }
        }

        return false;
    }





    //This class does drawing graphics and nothing else
    public class AnimationPanel extends JPanel {

        AnimationPanel() {
            this.setBackground(Color.BLACK);
            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
            this.setPreferredSize(new Dimension((int) screenSize.getWidth() / 2, (int) screenSize.getHeight() / 2));
        }


        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            for (MyImage im : imageList) {
                g.drawImage(im.image, im.x, im.y, im.width, im.height, null);
            }
        }
    }

    //this is the Runnable AnimationThread which does all the image moving and repainting.
    private class AnimationThread implements Runnable {
        @Override
        public void run() {
            while (!f.isCancelled()) {
                moveAllImages();
                animationPanel.repaint();

                try {
                    Thread.sleep(5);
                }
                catch (InterruptedException e) {
                    System.out.println(e.getMessage());
                    f.cancel(true);
                }

            }

        }

    }
}

这是MyImage类

package bouncer;

import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;


//NOTE: This class requires BouncingImages.java

/* This class combines an image with a rectangle
 * which allows for easy movement and collision detection
 */
class MyImage extends Rectangle {
    //this gives it an x,y,width,height

    static int biggestDim = 0;
    BufferedImage image;
    Color color = Color.RED;
    //these are the speeds of movement
    int vx = 1;
    int vy = 1;
    int lastx = -1;
    int lasty = -1;

    Point ctr, tl, tr, bl, br, ml, mr, mt, mb;

    MyImage(String filename) {

        vx = (int) (Math.random() * 5 + 1);
        vy = (int) (Math.random() * 5 + 1);

        //load the image
        try {
            image  = ImageIO.read(new File(filename));
            width  = image.getWidth(null);
            height = image.getHeight(null);
        }
        catch (IOException e) {
            System.out.println("ERROR: image file \"" + filename + "\" not found");
            //e.printStackTrace();
            BouncingImages.imagesLoaded = false;
            width                       = 100 + (int) (Math.random() * 100);
            height                      = width - (int) (Math.random() * 70);
            color                       = Color.getHSBColor((float) Math.random(), 1.0f, 1.0f);    // a quick way to get random colours
            //System.exit(0);
        }

        //update the variable containing the biggest dimension
        if (width > biggestDim) biggestDim = width;
        if (height > biggestDim) biggestDim = height;

        calculatePoints();
    }

    void calculatePoints() {
        //Calculate points
        //corners
        tl = new Point(x, y);
        tr = new Point(x + width, y);
        bl = new Point(x, y + height);
        br = new Point(x + width, y + height);
        //center
        ctr = new Point(x + width / 2, y + height / 2);
        //mid points of sides
        ml = new Point(x, y + height / 2);
        mr = new Point(x + width, y + height / 2);
        mt = new Point(x + width / 2, y);
        mb = new Point(x + width / 2, y + height);
    }

    void moveImage(BouncingImages.AnimationPanel panel) {
        lastx = x;
        lasty = y;
        x += vx;
        y += vy;
        if (x < 0 && vx < 0) {
            x  = 0;
            vx = -vx;
        }
        if (y < 0 && vy < 0) {
            y  = 0;
            vy = -vy;
        }
        if (x + width > panel.getWidth() && vx > 0) {
            x  = panel.getWidth() - width;
            vx = -vx;
        }
        if (y + height > panel.getHeight() && vy > 0) {
            y  = panel.getHeight() - height;
            vy = -vy;
        }
    }

    void undoMove() {
        if (lastx > 0) {
            x = lastx;
            y = lasty;
        }
        lastx = lasty = -1;
    }
}
java multithreading executorservice java-threads
1个回答
1
投票

您正在addImage中的方法Thread.sleep(50)中阻塞主线程。相反,您可以确保异步调用方法addImage,例如

if (e.getActionCommand().equals("Add")) {
   CompletableFuture.runAsync(this::addImage);
}
if (e.getActionCommand().equals("Add 10")) {
   CompletableFuture.runAsync(() -> addImages(10));
}

  private void addImages(int numberOfImages) {
    for(int i = 0; i < numberOfImages; i++) {
      addImage();
      startAnimation();
    }
  }

如果您想对线程做更多的试验,那么您可以考虑如何“手动”进行操作。

© www.soinside.com 2019 - 2024. All rights reserved.