简体   繁体   中英

java swing keylistener and threads

no, this is not the general question about threadsafety and adding of keylisteners :)

I encountered a strange problem when switching a boolean on and off by using a keylistener in swing: I turn the boolean on and off when the key is pressed and released. In another thread, I read the value of the boolean and want to do something with it. But somehow the if block is not being accessed though the value of the boolean must be true because I am holding the key. If I add breakpoints to this section, the breakpoint does get accessed, or if I add a sleep inside the if block. Why is this happening? Can someone explain?

EDIT: Strange. Using an AtomicBoolean solves the issue. Could this be a live lock?

package de.osipovan.rccontroller.gui;

import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class Test extends JFrame implements KeyListener {

    private static final long serialVersionUID = -8395585938567486151L;

    private JPanel contentPane;
    private boolean doIt = false;
    private Thread t = new Thread(new Runnable() {

        @Override
        public void run() {
            while(true){
                if(doIt){
                    System.out.println("done");
//                  try {
//                      Thread.sleep(10);
//                  } catch (InterruptedException e) {
//                      // TODO Auto-generated catch block
//                      e.printStackTrace();
//                  }
                }
            }
        }
    });

    public Test (){
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        contentPane.setLayout(new BorderLayout(0, 0));
        setContentPane(contentPane);

        t.start();

        addKeyListener(this);
        contentPane.addKeyListener(this);

        setVisible(true);
    }

    @Override
    public void keyTyped(KeyEvent e) {}

    @Override
    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_UP){
            doIt = true;
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_UP){
            doIt = false;
        }
    }

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

I think you can use the outer doIt variable with 'Test.doIt'.

In general you use OuterClassName.this to refer to the enclosing instance of the outer class.

I guess you should try a setter and a local variable in the thread(). This may need a new class. So everytime 'doit' changes you do like classname.setBool(doit). I'm not sure if it's working but it would be a nice start :)

I'm not very experienced with threads, but i would say "doIt" is not thread-safe.

You could also use volatile : volatile private boolean doIt = false; This will solve this issue. The volatile makes java always to re-read the value from memory, not caching or something..

But that's all i know. I hope someone can give a better explanation.

I would prefer AtomicBoolean because i've always read something like: "don't use volatile if you don't really need to do" and that's what AtomicBoolean is made for.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM