SwingWorker с ExecutionService и общим объектом - PullRequest
0 голосов
/ 13 января 2019

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

Краткое содержание программы

Класс PrimeSeekerTask генерирует простые числа между диапазонами. PrimeSeekerDisplay - это класс, который отображает работу, выполненную в PrimeSeekerTask. PrimeSeekerTask предназначен для выполнения «тяжелых» вычислений в фоновом режиме. Я обязан продлить SwingWorker для PrimeSeekerTask.

Во время выполнения программы должно быть несколько экземпляров PrimeSeekerTask, которые выполняют небольшую часть вычислений. Я обязан использовать ExecutionService.

Кроме того, программа должна запускаться при нажатии кнопки запуска и прерываться при нажатии кнопки отмены.

Когда я запускаю задачу как один фон SwingWorker, она отлично работает (как на этом снимке экрана) enter image description here

, но спецификация программы такова, что я должен был запустить много SwingWorker объектов.

Мои проблемы.

  1. Когда я пытаюсь использовать ExecutionService, программа зависает и потребляет почти 100% моего процессора, и ноутбук начинает плакать. Для небольших чисел, я получаю неправильные значения для процента и найденных простых чисел. Я думаю, некоторые ошибки синхронизации, но я подумал, Atomic исправления некоторые из них?

  2. Моя кнопка отмены не прерывает задачу.

Любая помощь будет оценена.

Ниже приведена моя подлинная попытка ( Я удалил импорт, чтобы сделать код короче )

public class PrimeSeekerDisplay extends JFrame
    implements ActionListener, PropertyChangeListener {

private JLabel topLabel;
private JTextArea textArea;
private JProgressBar progressBar;
private JButton startButton;
private JButton cancelButton;
private JLabel primesFoundLabel;

private final long max;
private final long chunkSize;
PrimeSeekerTask task = null;
private final int THREAD_NUMBER = 9;
ExecutorService executorService = Executors
        .newFixedThreadPool(THREAD_NUMBER);

/**
 * 
 */
private static final long serialVersionUID = 6602966318374691217L;

/**
 * 
 */
public PrimeSeekerDisplay(final long max, final long chunkSize) {
    super("Prime Seeker");
    this.max = max;
    this.chunkSize = chunkSize;
    initGui();

}

/*
 * initGui() creates all the components and lays them on the display
 */
private final void initGui() {

    // the top label
    topLabel = new JLabel("Primes in [1.." + String.valueOf(max) + "]");
    final JPanel topPanel = new JPanel();
    topPanel.add(topLabel);
    topPanel.setBorder(new EmptyBorder(5, 5, 5, 5));

    // the text area
    final JScrollPane textPane = createTextArea();

    // the progress bar
    progressBar = new JProgressBar(0, 100);
    progressBar.setStringPainted(true);
    progressBar.setBorder(BorderFactory.createLineBorder(Color.BLACK));

    // the buttons and items in the status pane
    startButton = createButton("Start");
    // enable this at the beginning of program.
    // This will be disabled when clicked
    startButton.setEnabled(true);

    cancelButton = createButton("Cancel");
    // Disable cancel before program starts.
    // Once program starts, this will be enabled
    cancelButton.setEnabled(false);

    // update this with number of primes found.
    // only print something here when atleast a prime is found
    primesFoundLabel = new JLabel();
    primesFoundLabel.setEnabled(false);

    final JPanel statusPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
    statusPanel.setBorder(new EmptyBorder(10, 0, 0, 0));
    statusPanel.add(startButton);
    statusPanel.add(cancelButton);
    statusPanel.add(primesFoundLabel);

    // Jpanel to hold all components
    final JPanel displayPanel = new JPanel();
    displayPanel.setLayout(new BoxLayout(displayPanel, BoxLayout.Y_AXIS));

    // add components to the display panel
    displayPanel.add(topPanel);
    displayPanel.add(textPane);
    displayPanel.add(progressBar);
    displayPanel.add(statusPanel);
    displayPanel.setBorder(new EmptyBorder(10, 10, 10, 10));

    // add the displayPanel to the frame
    add(displayPanel);
    setLocationRelativeTo(null);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    pack();
}

/**
 * Create the text area embedded inside a ScrollPane
 * 
 * @return a JScrollPane object
 */
private JScrollPane createTextArea() {
    textArea = new JTextArea(8, 40);
    textArea.setMargin(new Insets(5, 5, 5, 5));
    textArea.setLineWrap(true);
    textArea.setWrapStyleWord(true);
    textArea.setEditable(false);
    textArea.setLineWrap(true);
    textArea.setWrapStyleWord(true);
    final JScrollPane scrollPane = new JScrollPane(textArea);
    return scrollPane;
}

/**
 * A method for creating a JButton
 * 
 * @param text The text to display on the button This value in lower
 *             case is also the actionCommand
 * @return A JButton with text, actionCommand, and action listener set
 */
private JButton createButton(final String text) {
    final JButton button = new JButton(text);
    button.setActionCommand(text.toLowerCase());
    button.addActionListener(this);
    return button;
}

/*
 * (non-Javadoc)
 * 
 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.
 * ActionEvent)
 */
@Override
public void actionPerformed(ActionEvent e) {

    if (e.getActionCommand().equalsIgnoreCase("start")) {
        startButton.setEnabled(false);
        cancelButton.setEnabled(true);
        textArea.setText(null);
        primesFoundLabel.setText("Nothing found yet");

        // ####### Attempt to run many PrimeSeekerTasks fails ######
        // ****** i need help here and how to cancel the process///
        /*
        int chunk = (int) (max / chunkSize);

        System.out.println("chunk => " + chunk);
        int remainder = (int) (max % chunkSize);

        for (int index = 0; index < chunk; index++) {
            final long lower = index * chunkSize;
            final long upper = (index == chunk - 1
                    ? (lower + remainder + chunkSize)
                    : (lower + chunkSize));
            System.out.println("Index: " + index + " Lower: " + lower
                    + " upper: " + upper);
            PrimeSeekerTask primer = new PrimeSeekerTask(lower, upper);
            primer.addPropertyChangeListener(this);
            executorService.submit(primer);
        }

         */
        // ======= 
        //Running this works very well but it is not according to
        // program specs
        // ===========

          task = new PrimeSeekerTask(1, max);
          task.addPropertyChangeListener(this);
          task.execute();

    }
    if (e.getActionCommand().equalsIgnoreCase("cancel")) {
        if (task != null) {
            task.cancel(true);
        }
        startButton.setEnabled(true);
        cancelButton.setEnabled(false);
        //executorService.shutdownNow();
    }

}

/**
 * PrimeSeekerTask generatates prime numbers in a range and updates the
 * progressBar, textArea and primesFoundLabel
 * 
 * 
 * @author longb
 *
 */
private class PrimeSeekerTask extends SwingWorker<String, String> {

    private final long lowerRange;
    private final long upperRange;
    private final AtomicInteger progressMade = new AtomicInteger(0);
    private final AtomicLong primesFound = new AtomicLong(0);

    /**
     * @param upperRange
     * @param lowerRange
     */
    public PrimeSeekerTask(final long lowerRange, final long upperRange) {
        super();
        this.lowerRange = lowerRange;
        this.upperRange = upperRange;

    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.swing.SwingWorker#doInBackground()
     */
    @Override
    protected String doInBackground() throws Exception {
        while (!isCancelled()) {
            for (long lower = lowerRange; lower < upperRange; lower++) {

                if (isPrime(lower)) {
                    primesFound.getAndIncrement();
                    publish(String.valueOf(lower));
                    primesFoundLabel.setText(
                            "Primes found: " + primesFound.intValue());
                }
                progressMade.getAndIncrement();

                int progress = (int) (100 * (progressMade.intValue() + 1)
                        / upperRange);
                setProgress(progress);
            }

        }
        return "\n";
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.swing.SwingWorker#process(java.util.List)
     */
    @Override
    protected void process(List<String> chunks) {
        for (String string : chunks) {
            textArea.append(string + ", ");
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.swing.SwingWorker#done()
     */
    @Override
    protected void done() {
        startButton.setEnabled(true);
        cancelButton.setEnabled(false);
    }
}

private boolean isPrime(final long number) {
    final long limit = (long) Math.sqrt(number) + 1;

    if (number < 2) {
        return false;
    }

    for (long i = 2; i < limit; ++i) {
        if ((number % i) == 0) {
            return false;
        }
    }
    return true;
}

/*
 * (non-Javadoc)
 * 
 * @see java.beans.PropertyChangeListener#propertyChange(java.beans.
 * PropertyChangeEvent)
 */
@Override
public void propertyChange(PropertyChangeEvent evt) {
    if ("progress".equalsIgnoreCase(evt.getPropertyName())) {
        int progress = (int) evt.getNewValue();
        progressBar.setValue(progress);
    }
}

}

// в Run.java

public class Run {

/**
 * 
 */
public Run() {
    // TODO Auto-generated constructor stub
}

/**
 * @param args
 */
public static void main(String[] args) {

    if (args.length == 3) {
        for (int i = 0; i < args.length; i++) {
            System.out.println("arg#" + i + " : " + args[i]);
        }
        try {
            long max = Integer.valueOf(args[1]);
            long chunksize = Integer.valueOf(args[2]);
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    PrimeSeekerDisplay display = new PrimeSeekerDisplay(max,
                            chunksize);
                    display.setVisible(true);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    } else {
        System.err.println("\nUsage: lab8 maxValue chunkSize\n");
    }
}

}

Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 13 января 2019

Этот ответ относится к первой опубликованной проблеме: «Я получаю неправильные значения для процента и найденных простых чисел».
Размещенный код отображает правильный прогресс и количество найденных значений.
Я считаю, ExecutionService следует использовать следующим образом:

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;

public class PrimeSeekerDisplay extends JFrame implements ActionListener, PropertyChangeListener {

    private JLabel topLabel;
    private JTextArea textArea;
    private JProgressBar progressBar;
    private JButton startButton;
    private JButton cancelButton;
    private JLabel primesFoundLabel;
    private final long max;
    //list to hold all tasks
    private final List<PrimeSeekerTask> tasks;
    private final int numberOfTasks;
    private final ExecutorService executorService ;

    /**
     *
     */
    public PrimeSeekerDisplay( long max, long chunkSize) {
        super("Prime Seeker");
        this.max = max;
        numberOfTasks = max/chunkSize >= 1L ? (int) (max/chunkSize) : 1;
        executorService = Executors.newFixedThreadPool(numberOfTasks);
        tasks = new ArrayList<>(numberOfTasks);
        initGui();
    }

    /*
     * initGui() creates all the components and lays them on the display
     */
    private final void initGui() {

        // the top label
        topLabel = new JLabel("Primes in [1.." + String.valueOf(max) + "]");
        final JPanel topPanel = new JPanel();
        topPanel.add(topLabel);
        topPanel.setBorder(new EmptyBorder(5, 5, 5, 5));

        // the text area
        final JScrollPane textPane = createTextArea();

        // the progress bar
        progressBar = new JProgressBar(0, 100);
        progressBar.setStringPainted(true);
        progressBar.setBorder(BorderFactory.createLineBorder(Color.BLACK));

        // the buttons and items in the status pane
        startButton = createButton("Start");
        // enable this at the beginning of program.
        // This will be disabled when clicked
        startButton.setEnabled(true);

        cancelButton = createButton("Cancel");
        // Disable cancel before program starts.
        // Once program starts, this will be enabled
        cancelButton.setEnabled(false);

        // update this with number of primes found.
        // only print something here when atleast a prime is found
        primesFoundLabel = new JLabel();
        primesFoundLabel.setEnabled(false);

        final JPanel statusPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        statusPanel.setBorder(new EmptyBorder(10, 0, 0, 0));
        statusPanel.add(startButton);
        statusPanel.add(cancelButton);
        statusPanel.add(primesFoundLabel);

        // Jpanel to hold all components
        final JPanel displayPanel = new JPanel();
        displayPanel.setLayout(new BoxLayout(displayPanel, BoxLayout.Y_AXIS));

        // add components to the display panel
        displayPanel.add(topPanel);
        displayPanel.add(textPane);
        displayPanel.add(progressBar);
        displayPanel.add(statusPanel);
        displayPanel.setBorder(new EmptyBorder(10, 10, 10, 10));

        // add the displayPanel to the frame
        add(displayPanel);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        pack();
        setVisible(true);
    }

    /**
     * Create the text area embedded inside a ScrollPane
     *
     * @return a JScrollPane object
     */
    private JScrollPane createTextArea() {
        textArea = new JTextArea(8, 40);
        textArea.setMargin(new Insets(5, 5, 5, 5));
        textArea.setLineWrap(true);
        textArea.setWrapStyleWord(true);
        textArea.setEditable(false);
        textArea.setLineWrap(true);
        textArea.setWrapStyleWord(true);
        final JScrollPane scrollPane = new JScrollPane(textArea);
        return scrollPane;
    }

    /**
     * A method for creating a JButton
     *
     * @param text The text to display on the button This value in lower
     *             case is also the actionCommand
     * @return A JButton with text, actionCommand, and action listener set
     */
    private JButton createButton(final String text) {
        final JButton button = new JButton(text);
        button.setActionCommand(text.toLowerCase());
        button.addActionListener(this);
        return button;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.
     * ActionEvent)
     */
    @Override
    public void actionPerformed(ActionEvent e) {

        if (e.getActionCommand().equalsIgnoreCase("start")) {
            startButton.setEnabled(false);
            cancelButton.setEnabled(true);
            textArea.setText(null);
            primesFoundLabel.setText("Nothing found yet");

            int chunk = (int) (max / numberOfTasks);

            System.out.println("chunk => " + chunk);
            int remainder = (int) (max % numberOfTasks);

            long lower = 1, upper = chunk + remainder;
            for(int taskNum = 0; taskNum < numberOfTasks; taskNum++){
                System.out.println("Index: " + taskNum + " Lower: " + lower  + " upper: " + upper);
                PrimeSeekerTask task = new PrimeSeekerTask(lower, upper, executorService);
                tasks.add(task);
                lower = upper +1 ;
                upper = lower + chunk > max ? max : lower + chunk;
                task.addPropertyChangeListener(this);
                task.setListener(()-> updatePrimesFound() );
                task.execute();
            }
        }

        if (e.getActionCommand().equalsIgnoreCase("cancel")) {

            for(PrimeSeekerTask task : tasks) {
                task.cancel(true);
            }

            startButton.setEnabled(true);
            cancelButton.setEnabled(false);
            executorService.shutdownNow();
        }
    }

    private boolean isPrime(final long number) {
        final long limit = (long) Math.sqrt(number) + 1;

        if (number < 2)
            return false;

        for (long i = 2; i < limit; ++i) {
            if (number % i == 0)
                return false;
        }
        return true;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.beans.PropertyChangeListener#propertyChange(java.beans.
     * PropertyChangeEvent)
     */
    @Override
    public synchronized void propertyChange(PropertyChangeEvent evt) {
        if ("progress".equalsIgnoreCase(evt.getPropertyName())) {
            updateProgress();
        }
    }

    private synchronized void updateProgress(){

        int totalProgress = 0; //accumulate progress from all tasks
        for(PrimeSeekerTask task : tasks){
            totalProgress += task.getProgress();
        }
        progressBar.setValue(totalProgress/numberOfTasks);
    }

    //update the number of promes found
    private synchronized void updatePrimesFound(){
        int totalPrimesFound = 0;  //accumulate primes found from all tasks
        for(PrimeSeekerTask task : tasks){
            totalPrimesFound += task.getPrimesFound();
        }
        primesFoundLabel.setText("Primes found: "+totalPrimesFound );
    }

    public static void main(String[] args) {
        new PrimeSeekerDisplay(10000000, 5000000);
    }

    /**
     * PrimeSeekerTask generatates prime numbers in a range and updates the
     * progressBar, textArea and primesFoundLabel
     *
     * @author longb
     *
     */
    class PrimeSeekerTask extends SwingWorker<String, String> {

        private final long lowerRange, upperRange;
        private int progressMade = 0, progress = 0, primesFound = 0;
        private final ExecutorService executorService;
        private Listener listener;

        /**
         * @param upperRange
         * @param lowerRange
         * @param executorService
         */
        public PrimeSeekerTask(final long lowerRange, final long upperRange, ExecutorService executorService) {
            this.executorService = executorService;
            this.lowerRange = lowerRange;
            this.upperRange = upperRange;
        }

        /*
         * (non-Javadoc)
         *
         * @see javax.swing.SwingWorker#doInBackground()
         */
        @Override
        protected String doInBackground() throws Exception {

            Future<String> f = executorService.submit(() ->{

                for (long lower = lowerRange; lower < upperRange; lower++) {
                    if (isCancelled() ) { break;}
                    if (isPrime(lower)) {
                        primesFound++;
                        publish(String.valueOf(lower));
                        if(listener != null)
                         {
                            listener.updatePrimesFound(); //trigger update
                        }
                    }

                    int updatedProgress = (int) (100 * ++ progressMade/ (upperRange-lowerRange));
                    if(updatedProgress == progress)  {
                        continue;  // reduce the number of updates
                    }

                    progress = updatedProgress;
                    setProgress(progress);
                }
                return "\n";
            });

            return f.get();
        }

        /*
         * (non-Javadoc)
         *
         * @see javax.swing.SwingWorker#process(java.util.List)
         */
        @Override
        protected void process(List<String> chunks) {
            for (String string : chunks) {
                textArea.append(string + ", ");
            }
        }

        /*
         * (non-Javadoc)
         *
         * @see javax.swing.SwingWorker#done()
         */
        @Override
        protected void done() {
            startButton.setEnabled(true);
            cancelButton.setEnabled(false);
        }

        int getPrimesFound() {

            return primesFound;
        }

        void setListener(Listener listener) {
            this.listener = listener;
        }
    }
}

interface Listener {
    void updatePrimesFound();
}
0 голосов
/ 13 января 2019

Этот ответ относится ко второй опубликованной проблеме: «Моя кнопка отмены не прерывает задачу».
У вас есть while цикл, который должен остановиться, когда isCancelled() вернет false:

`while (! isCancelled())`

проблема в том, что за ним следует цикл for. Условие остановки оценивается только после завершения цикла for.
Чтобы преодолеть это просто добавьте

if (isCancelled() ) { break;} 

в цикле for.

Первая проблема требует дальнейшего изучения.
(обратите внимание на один вопрос для каждой почтовой политики )

...