Java Swing: Improving cursor response using Thread

2019-08-02 18:43发布

I need to speed up the cursor response which is hovering over a bunch of rectangles. As the rectangles increase in number(or complexity), the straight forward linear algorithm of iterating over the rectangles starts to deteriorate.

This hyperlink shows a linear search model(remember to add the break keyword!!!)linear approach

To clearly show what I mean,below is code of a JPanel that has two groups of rectangles.The first group is a List of rectangles on the y-axis, the second group is a List of rectangles on the x-axis.

Keep in mind, that my solution is the best I could think of, of using Two threads to concurrently iterate; one thread per List,one for x-axis and other for y-axis. The sub-optimal solution that I abandoned(code not displayed) is where there was a loop within a loop.One loop dedicated for the x-axis rectangles and the other for y-axis rectangles.This,I deemed sub-optimal as searching for a y-axis rectangle through the contain() method (assuming the inner loop is dedicated for y-axis rectangles) imposed an undue overhead by looping over all x-axis rectangles first.

To improve the linear model i thought of this threaded approach to simultaneously engage worker threads.

Btw, am a java novice,especially in multi-threading.

Problem: One thread seems to disturb the performance of the other.The algorithm below is implemented by two wrapper classes extending Thread which both have a method of iterating over rectangles.They each both work excellently if the other is commented out,i.e each works well individually and separately ,but one takes a performance hit when both start at the same time.

If I join() one thread,isn't that reverting to a linear model which is poor search algorithm as rectangles grow in complexity?

(complexity means not only horizontal and vertical, but also diagonal e.t.c rectangles)?

public class FlyingSaucer extends JPanel {
    Rectangle2D rec;
    Rectangle2D rec1; 
    List<Rectangle2D> recList;
    List<Rectangle2D> recList2;

    Rectangle2D.Double mouseBoxx;  
    int f = 10;
    int g = 0;
    int s = 10;
    int y = 5;
    int z = 500;

    public FlyingSaucer(){

        //FlyingSaucer needs to quickly identify specific points over given areas
        //enclosed in rectangles.They use a 'divide and conquer' approach where 
        //different types of rectangles are first identified and a worker thread
        //assigned to each category

        mouseBoxx = new Rectangle.Double();
        recList = new ArrayList<>();
        recList2 = new ArrayList<>();

        for(int i = 0; i < 15; i++){
            rec = new Rectangle2D.Double(2+f,10+g,5,1000);       
            f +=50;
            recList.add(rec);                
        }
        f = 10;

        for(int i = 0; i < 20; i++){
            rec1 = new Rectangle2D.Double(2+y,10+s,1000,5);       
            s +=35;
            recList2.add(rec1);                
        }
        s = 10;
    }


    public static void main(String[] args) {
        JFrame frame = new JFrame();
        FlyingSaucer fs = new FlyingSaucer();
        Laser laser = new Laser(fs);
        fs.addMouseMotionListener(laser);
        fs.addMouseListener(laser);
        frame.getContentPane().add(fs);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(700,700);
        frame.setVisible(true);     
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); 
        ((Graphics2D)g).setColor(Color.RED);
        int a = 10;
        int b = 10;

        for(int i = 0;i < recList.size();i++){               
           ((Graphics2D)g).draw(recList.get(i));
        }

        for(int i = 0;i < recList2.size();i++){               
           ((Graphics2D)g).draw(recList2.get(i));
        }
    }
}


class Laser implements MouseListener,MouseMotionListener{
    Rectangle2D.Double mouseBox;
    Rectangle2D.Double recx;
    Rectangle2D.Double recy;
    FlyingSaucer fs;
    public Laser(FlyingSaucer fs){
        this.fs = fs;
    }

    @Override public void mouseClicked (MouseEvent e) { }
    @Override public void mousePressed (MouseEvent e) { }
    @Override public void mouseReleased(MouseEvent e) { }
    @Override public void mouseEntered (MouseEvent e) { }
    @Override public void mouseExited  (MouseEvent e) { }
    @Override public void mouseDragged (MouseEvent e) { }

    @Override
    public void mouseMoved(MouseEvent e) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                double x = e.getX();
                double y = e.getY();

                mouseBox = fs.mouseBoxx;
                mouseBox = new Rectangle2D.Double(x-5,y-5,10,10);

                Graphics g = fs.getGraphics();
                ((Graphics2D)g).setColor(Color.BLACK);
                ((Graphics2D)g).draw(mouseBox);

                //thread one for horizontal rectangles
                HorizontalBuffer hb = new HorizontalBuffer(fs,e);
                hb.start();

                //thread two for vertical rectangles
                VerticalBuffer vb = new VerticalBuffer(fs,e);
                vb.start();

                fs.repaint();
                g.dispose();
        }});
    }

    class HorizontalBuffer extends Thread{
        FlyingSaucer fs;
        MouseEvent e;
        List<Rectangle2D> recX;

        public HorizontalBuffer(FlyingSaucer fs,MouseEvent e){
            this.fs = fs;
            this.e = e;
        }

        public void run() {
            recX = fs.recList;
            Iterator <Rectangle2D> recs = recX.iterator();  
            int v = 1;
            while(recs.hasNext()){
                recx = (Rectangle2D.Double) recs.next();
                if(recx.contains(e.getPoint())){
                    System.out.println("X rectangle detected.."+v);
                     fs.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                    break;
                }
                else {fs.setCursor(Cursor.getDefaultCursor());}

                v++;
            }
        }
    }

    class VerticalBuffer extends Thread{
        FlyingSaucer fs;
        MouseEvent e;
        //List<Rectangle2D> recX;
        List<Rectangle2D> recY;

        public VerticalBuffer(FlyingSaucer fs,MouseEvent e){
            this.fs = fs;
            this.e = e;
        }

        public void run(){
            recY = fs.recList2;
            Iterator <Rectangle2D> recs = recY.iterator();  
            int v = 1;
            while(recs.hasNext()){
                recy = (Rectangle2D.Double) recs.next();
                if(recy.contains(e.getPoint())){
                    System.out.println("Y rectangle detected.."+v);
                    fs.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                    break;
                }
                else {fs.setCursor(Cursor.getDefaultCursor());}

                v++;
            }
        }
    }
}

1条回答
爷的心禁止访问
2楼-- · 2019-08-02 19:23

Your FlyingSaucer class is not thread safe, but you are accessing it from two different threads, HorizontalBuffer and VerticalBuffer. This is likely to cause problems: for example, when HorizontalBuffer tries to get the FlyingSaucer reclist, it might be getting null and throwing an exception. Other problems could also occur due to unsynchronized access. This isn't the easiest thing to debug: for example, you won't know about the exception unless you are watching the console, since an exception in one thread doesn't terminate a multithreaded program.

You could fix this particular access synchronization problem by making FlyingSaucer thread safe, by properly synchronizing access to it. However, there may also be other problems; getting a multithreaded application working properly is not simple, and might not be the best approach for a Java novice, which you say you are.

As noted by others in the comments, it's likely that the best solution is not multithreading. I'd suggest posting a new question with your single threaded solution and asking how to speed it up. You can mention multithreading in that question, but I'd recommend being open to other solutions as well.

查看更多
登录 后发表回答