Проблемы с потоками в Java - PullRequest
2 голосов
/ 12 сентября 2010

У меня следующая проблема. Мое приложение J2ME полностью погибло. Я думал, что это тупик, но, используя встроенную функциональность NetBeans, он не смог найти такого.

Однако через некоторое время он начал выдавать следующие сообщения в консоли:

8242276 - CORE - CRITICAL - 2 - **event queue 3 full, dropping event<br> 8242284 - CORE - CRITICAL - 2 - **event queue 3 full, dropping event<br> 8242292 - CORE - CRITICAL - 2 - **event queue 3 full, dropping event<br> 8242340 - CORE - CRITICAL - 2 - **event queue 3 full, dropping event<br> 8242540 - CORE - CRITICAL - 2 - **event queue 3 full, dropping event<br> 8242628 - CORE - CRITICAL - 1 - **event queue 3 full, dropping event<br> 8242636 - CORE - CRITICAL - 1 - **event queue 3 full, dropping event<br> 8242644 - CORE - CRITICAL - 1 - **event queue 3 full, dropping event

и пр.

Любые идеи относительно того, что может быть в случае, если это не тупик (по крайней мере, NetBeans говорит так), будут высоко оценены.

Спасибо!

1 Ответ

2 голосов
/ 12 сентября 2010

Когда вы используете AWT или Swing, в фоновом режиме работает специальный поток, называемый EventQueue. Когда вы добавляете прослушиватель к компоненту (например, ActionListener для JButton) и метод слушателя событий (например, actionPerformed), метод выполняется в потоке EventQueue. Вы можете выполнить что-то в потоке EventQueue, если текущий поток не является потоком EventQueue, запустив

EventQueue.invokeLater(new Runnable(){ //this could also be SwingUtilities.invokeLater() instead
    public void run(){
        ...
    }
});

Это здорово. Проблема возникает, когда у вас есть событие снова и снова и снова. Когда это происходит, поток EventQueue не может выполнить код для предыдущего события достаточно быстро, чтобы выполнить код для следующего события. Поэтому он помещает событие в очередь, ожидая завершения текущего обработчика событий. Это может произойти, когда вы выполняете большую операцию, такую ​​как чтение из файла, в потоке EventQueue. Например:

ActionListener l = new ActionListener(){
    public void actionPerformed(ActionEvent ae){
        try {
            BufferedReader in = new BufferedReader(new FileReader("foo.txt"));
            StringBuffer sb = new StringBuffer();
            char[] buf = new char[4096];
            int n;
            while (-1 != (n = in.read(buf))){
                sb.append(buf, 0, n);
            }
            in.close();
            String s = sb.toString();
            jTextPane1.setText(s);
        } catch (IOException ioe){
            ioe.printStackTrace();
        }

    }
};
jButton1.addActionListener(l);  

Весь код внутри actionPerformed выполняется в потоке EventQueue. Никакие другие события не могут быть обработаны, пока этот код не завершен. Если foo.txt большой файл, то до завершения кода пройдет некоторое время. Это означает, что каждый раз, когда вы нажимаете jButton1 до завершения кода, он добавляет новое событие в EventQueue. Поэтому, если вы несколько раз нажмете jButton1, то в очередь будет добавляться все больше и больше событий. Вы не можете переполнить его таким образом, потому что вы не можете щелкать достаточно быстро и достаточно долго, не сдаваясь. Но если и событие, такое как движение мыши, которое происходит в каждом пикселе, приводит к тяжелому событию. Тогда это может вызвать огромную очередь EventQueue, и это может даже вызвать переполнение EventQueue. Чтобы решить эту проблему, можно запустить другой поток, как только произойдет событие. Например:

final Runnable r = new Runnable() {
    public void run() {
        try {
            BufferedReader in = new BufferedReader(new FileReader("foo.txt"));
            StringBuffer sb = new StringBuffer();
            char[] buf = new char[4096];
            int n;
            while (-1 != (n = in.read(buf))) {
                sb.append(buf, 0, n);
            }
            in.close();
            final String s = sb.toString();
            EventQueue.invokeLater(new Runnable(){
                public void run(){
                    jTextPane1.setText(s);
                }
            });
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
};
ActionListener l = new ActionListener(){
    public void actionPerformed(ActionEvent ae){
        new Thread(r).start();
    }
};
jButton1.addActionListener(l);  

Причина, по которой необходимо вызывать что-либо в потоке EventQueue, например,

EventQueue.invokeLater(new Runnable(){
    public void run(){
        jTextPane1.setText(s);
    }
});  

Это потому, что в противном случае экран не обновился бы. Например, если поддерживать индикатор выполнения, EventQueue.invokeLater() будет обновлять экран СЕЙЧАС, но если он не будет заключен в EventQueue.invokeLater(), он будет ждать, пока поток завершит работу, прежде чем обновит экран. Это бесполезно, когда дело доходит до индикаторов прогресса. В этом случае это, вероятно, не очень много.

Спасибо за отличный вопрос!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...