首页 > 解决方案 > 线程对另一个线程有影响?

问题描述

因此,我想为每个任务制作一个带有内置计时器的任务列表,以用于培训目的。这个想法是我可以启动一个顶级任务,并且在该任务的计时器完成后,如果子任务附加到顶级任务,则将启动一个子任务。我使用 Java Swing 来提供一个可以启动任务的 GUI。每个任务都应该创建一个自己的Timer类实例来测量任务的持续时间。该TaskObject课程通过startTask().

问题:

当我附加一个子任务时,子任务会启动,但它的计时器在完成后不会停止。它到达了while循环的末尾,但不知何故它不断地进入和离开循环,我不知道为什么会这样。单独的顶级任务运行得非常好。计时器在给定的持续时间后停止。也许你可以帮我在这里?

提前致谢!这是我的代码的简短版本:

public class Timer {

    private Thread watchThread;
    private long deltaTime;
    private volatile boolean run;
    private long time1, time2;

    ITaskObject task;

    public Timer(ITaskObject task){
        super();
        this.deltaTime = 0;
        this.run = false;
        this.task = task;
    }

    public void start(){
        createThread();
        run = true;
        watchThread.start();
    }

    public void stop(){
        watchThread.interrupt();    
        run = false;
    }

    public void reset(){
        run = false;
        time1 = 0;
        deltaTime = 0;
    }

    private void createThread(){
        watchThread = new Thread(() -> {
            time1 = System.currentTimeMillis();
            while(run){
                time2 = System.currentTimeMillis();
                deltaTime = time2 - time1;
                task.addTime((double)deltaTime);
                time1 = time2;
            }

        });
    }
}
/* imports */

public class TaskObject extends JPanel implements ITaskObject{

    private String taskName = "default";
    private double millisMax = 0;
    private double currentMillis;
    private Timer timer;
    private State state;
    private FontMetrics metrics;
    private Font myFont;
    private static final long serialVersionUID = 1L;
    private TaskObject nextSubtask;

    public TaskObject(String taskName, int millisMax){
        super();
        setSize(500, 70);
        this.currentMillis = 0;
        this.nextSubtask = null;
        this.state = State.UNDEF;
        this.taskName = taskName;
        this.millisMax = millisMax;
        this.myFont = new Font("TimesRoman", Font.PLAIN, 20);   
    }

    @Override
    public void startTask(){
        state = State.RUNNING;
        timer = new Timer(this);
        timer.start();
    }

    @Override
    public void addTime(double millis){
        currentMillis += millis;
        if(currentMillis >= millisMax){
            stopTask();
            currentMillis = millisMax;
            startSubtask();
        }
        this.repaint();
    }

    @Override
    public void stopTask(){
        state = State.STOPPED;
        timer.stop();
        timer.reset();
        repaint();
    }

    @Override 
    public void resetTask(){
        currentMillis = 0;
        stopTask();
        state = State.UNDEF;
    }

    @Override
    public void resumeTask() {
        timer.start();
        state = State.RUNNING;
    }

    @Override
    public void startSubtask() {
        if(nextSubtask != null){
            nextSubtask.startTask();
        }
    }

    @Override
    public void attachSubtask(TaskObject task) {
        nextSubtask = task;
    }
}

/* imports */

public class GUI extends JFrame{

    private int wX, wY;
    private TaskObject task;
    private PopUpMenu contextMenu;
    private static final long serialVersionUID = 1L;

    public static void main(String[] args) {
        new GUI(1200, 700);
    }

    public GUI(int wX, int wY){

        /*
         *
         */

        task = new TaskObject("Light Training", 3000);
        /* some settings for the panel itself */
        add(task);
        task.repaint();
        task.addMouseListener(new MouseAdapter(){
            public void mousePressed(MouseEvent me) {
                if(me.getButton() == MouseEvent.BUTTON1){
                    switch(task.getState()){
                        case UNDEF:
                            int answer = JOptionPane.showConfirmDialog(GUI.this, "Do you want to start this task?", "Confirmation", JOptionPane.OK_CANCEL_OPTION);
                            if(answer == JOptionPane.OK_OPTION){
                                task.startTask();
                            }
                        case STOPPED: 
                            task.resumeTask();
                            System.out.println("Resumed task");
                        case RUNNING:
                        default:
                    }

                }
            }
        });

        TaskObject task2 = new TaskObject("Subtask 1", 2000);
        /* settings for the panel */
        add(task2);
        task2.repaint();
        task2.addMouseListener(new MouseAdapter(){
            /* same as above, theoretically runnable from here */
        });

        /* the next task to be started if the top-level task has finished */
        task.attachSubtask(task2);

        /* */
    }
}

标签: javamultithreading

解决方案


The addTime method will always call the startSubtask method if it finds that the current task is out of time. That means that it is constantly telling the subtask to start, even after the subtask has already finished.

What I would suggest doing, is checking the state of the subtask in the addTime method: if the subTask is STOPPED, then do not start it again, as it has already run:

@Override
public void addTime(double millis) {
    currentMillis += millis;
    if (currentMillis >= millisMax) {
        stopTask();
        currentMillis = millisMax;
        // Only start the subtask if it is new or has been reset
        if (subtask.state == State.UNDEF) {
            startSubtask();
        }
    }
    this.repaint();
}

Another option is to only execute the addTime method if the task is currently in the RUNNING state:

@Override
public void addTime(double millis) {
    if (state == State.RUNNING) {
        currentMillis += millis;
        if (currentMillis >= millisMax) {
            stopTask();
            currentMillis = millisMax;
            startSubtask();
        }
    }
    this.repaint();
}

推荐阅读