簡體   English   中英

Java | 如何讓其中一個線程休眠而其他線程仍在運行?

[英]Java | How can I make one of the threads sleep while the other ones are still running?

我目前正在開發一個應用程序,它使用 draw function 在 JPanel 中為“彈跳圖像”設置動畫。 為了完成它,我必須學會使用線程。 當我在我的代碼中使用它們時,有人建議我可以使用 Executor 框架和 ExecutorService 之類的東西,而不是直接使用線程。

現在我的問題是,在添加新圖像時,我需要確保它們不會在彼此內部創建。 當程序檢測到它們將相交時,它應該等待一段時間,同時所有其他線程繼續運行,因此圖像仍在當前位置移動和繪制。 然而發生的事情是,當我讓其中一個線程休眠以等待某個位置為空時,整個程序似乎凍結了。 唯一似乎正在運行的是移動 function 的圖像。

Github Gist 中的代碼

這是代碼:

這是 BouncingImages class


/* 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 class

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;
    }
}

您在Thread.sleep(50)的方法addImage中阻塞了主線程。 相反,您可以確保異步調用方法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();
    }
  }

如果您想對線程進行更多實驗,那么您可以考慮如何“手工”完成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM