#Java Threads - How to interrupt and stop a thread while It's sorting an array?

109 messages · Page 1 of 1 (latest)

sterile crescent
#

I'm working on this portfolio project where java displays an array being sorted in real time.
On feature I want to add is a "reset" button that tells the app to stop sorting an array.
But I got seriously stuck and I think I'm going about this ALL WRONG.

I have two classes that I'm trying to implement this feature with.
AnimationUnit class and SortingAlgorithm class that has a "sortList" method.
What I was trying to do was store the thread that's sorting the array into a variable.
Then using a "reset()" method, call the "interrupt()" on the Thread object when I want to.

The sortList() method periodically checks the interrupt flag and throws an InterruptedException if it is interrupted.
But when I call the reset(), the sorting thread still seems to be alive because It's still sending updated to the GUI display.

Has anyone tried implementing something like this before?

dawn zephyrBOT
#

<@&987246399047479336> please have a look, thanks.

sterile crescent
#

Here's the beginSort() thread that stores the Thread in an AnimationUnit:

// commands the sorter to begin sorting the copyArray.
    public void beginSort(){

        // if null or terminated. assign new thread.
        if(runningSortingAlgorithmThread == null ||(runningSortingAlgorithmThread.isAlive()== false)){
            // store method call as an object. Using lambda
            this.runningSortingAlgorithmThread = new Thread(()-> 
            {
                try {
                    sortingAlgorithm.sortList(copyArray);
                } catch (InterruptedException e) {
                    System.out.println("ANIMATIONUNIT SORTING INTERRUPTED:" + this);
                    // set variable to null.
                    this.runningSortingAlgorithmThread = null;
                }
            });
        }

        // do nothing if thread is running or null.
        if(runningSortingAlgorithmThread.isAlive()){
            System.out.println("beginSort request rejected.");
            return;
        }
        else{
            // start running it is not alive.
            runningSortingAlgorithmThread.start();
        }
    }
sterile crescent
#

of course I have one implemented for Bubble sort

outer girder
#

also show reset

sterile crescent
#

Will do

#

SortList()

#

        int length = listArg.size();
        boolean swapped = false; // false if passes through array without swapping.

        // i is current iteration, j is current index.
        // outter loop (i)
        for (int i = 0; i < length - 1; i++) {

            // checks if interrupted, throws interrupt exception if true
            checkForInterrupts();       

            swapped = false;
            // inner loop(j)
            for(int j=0; j < length - i - 1; j++)
            {   
                checkForInterrupts(); 
                // notify comparison taking place.
                notifyObservers(this, new SortingEvent(DurationType.FULL_STEP,ActionType.SCANNING,j,(j+1)));
                // returns num greater than 0 if j bigger than j+1
                if(listArg.get(j).compareTo( listArg.get(j+1)) > 0)
                {
                    swap(j,listArg);
                    //System.out.println( i + " - " + j + ": " + listArg + " - Swapped (" + j + ") " + listArg.get(j+1) + " with (" + (j+1) + ") " + listArg.get(j) + "\n");
                    swapped = true;
                }
            }
            if(!swapped)
            {
                break;
            }
        }

        return listArg;
    }```
#

The sortList() method used by the sorting algorithm class checks for the interrupt flag while running.

    public ArrayList<T> sortList(ArrayList<T> listArg) throws InterruptedException {


        int length = listArg.size();
        boolean swapped = false; // false if passes through array without swapping.


        // i is current iteration, j is current index.
        // outter loop (i)
        for (int i = 0; i < length - 1; i++) {


            // checks if interrupted, throws interrupt exception if true
            checkForInterrupts();      


            swapped = false;
            // inner loop(j)
            for(int j=0; j < length - i - 1; j++)
            {  
                checkForInterrupts();
                // notify comparison taking place.
                notifyObservers(this, new SortingEvent(DurationType.FULL_STEP,ActionType.SCANNING,j,(j+1)));
                // returns num greater than 0 if j bigger than j+1
                if(listArg.get(j).compareTo( listArg.get(j+1)) > 0)
                {
                    swap(j,listArg);
     
                    swapped = true;
                }
            }
            if(!swapped)
            {
                break;
            }
        }


        return listArg;
    }
#

**Here's the reset() Thread: **

// returns true if interrupted, false if no thread is running.
    public boolean reset(){
        if(runningSortingAlgorithmThread == null){
            return false;
        }
        // first interrupt the thread.
        System.out.println("Interrupt sent to thread");
        runningSortingAlgorithmThread.interrupt();

        this.copyArray = new ArrayList<Integer>(this.focusArray);
        // must update bar graph gui with new copyArray.
        this.barGraph.setFocusArray(this.copyArray);

        // redraw graph.
        updateGraph();

        return true;
    }
sterile crescent
#

sorry if Its too messy to make out.

#

I have a feeling I'll have to do a complete overhall for this one.

outer girder
#

so

#

instead of doing all of that

#

you could just sort in advance

#

and record a replay

#

and then the gui just plays the replay

#

@sterile crescent

sterile crescent
#

And If the answer is yes, I'd like to know how to do it you know?

outer girder
sterile crescent
#

Or am I misunderstanding what your saying?

outer girder
dry needle
#

You can just exit the threads execution to stop it.

sterile crescent
outer girder
#

show checkForInterrupts

sterile crescent
#

But It does show me this in the console: java.lang.InterruptedException: sleep interrupted

#

So I know its sleeping is interrupted, I THINK at least.

outer girder
#

you shouldn't use interrupts imo

#

set a boolean flag yourself

sterile crescent
#

// throw interrupt if interrupted.
public void checkForInterrupts() throws InterruptedException{
if(Thread.currentThread().isInterrupted()){
throw new InterruptedException();
}
}

sterile crescent
outer girder
#

I always just use a flag

sterile crescent
#

oh

pastel saffron
dry needle
#

Yes, really.

pastel saffron
dry needle
#

You do it from inside the thread, just like a normal flow of execution. You're talking about killing threads from outside that thread.

pastel saffron
#

That being said @sterile crescent is the reset function called from outside the running thread? If so a volatile boolean flag is probably the easier and intuitive approach here

dry needle
dawn zephyrBOT
# dry needle

I uploaded your attachments as Gist. This makes them more accessible, for example to mobile users.

dry needle
#
            while (state.get() == State.RUNNING) {
              // do a slice of work.
              count++;
              try {
                barrier.await(); // wait until other threads are done
              } catch (InterruptedException | BrokenBarrierException e) {
                throw new RuntimeException(e);
              }
            }

You could also use CyclicBarrier. Here all threads wait until they hit the barrier and the barrier has the desired amount of threads waiting on it (4 in this case.) You can also run some code after each sync, seems pretty nice.

#

CyclicBarrier barrier = new CyclicBarrier(4, () -> count++);

sterile crescent
dry needle
#

Here it just counts each time.

#

It's a thread safe boolean

#

I'm using AtomicReference of an enum here but you could use volatile boolean.

sterile crescent
#

Counts each time a slice of work is done by the thread?

dry needle
#

Read the docs, it allows you to run some code after each set of threads does their slice of work. So for example, they could all try to sort one number might be their slice of work, maybe they are counting swaps and compare operations done. Then in this collating operation done (instead of just count++) you can collect that info.

#

For comparing sort algos, usually you want to know how many swap and compares are done.

pastel saffron
#
public class SortingExample<T> {
    volatile boolean stopSorting = false; //notifier to tell the sorting thread to stop sorting


    //Returns if we finished sorting or not
    public ArrayList<T> sortList(ArrayList<T> listArg) throws InterruptedException {


        int length = listArg.size();
        boolean swapped = false; // false if passes through array without swapping.


        // i is current iteration, j is current index.
        // outter loop (i)
        for (int i = 0; i < length - 1; i++) {


            // checks if we've been signalled to stop sorting, throws interrupt exception if true
            if (stopSorting) {
                return listArg;
            }


            swapped = false;
            // inner loop(j)
            for(int j=0; j < length - i - 1; j++)
            {
                if (stopSorting) {
                    return listArg; //We didn't fully sort
                }
                // notify comparison taking place.
                notifyObservers(this, new SortingEvent(DurationType.FULL_STEP,ActionType.SCANNING,j,(j+1)));
                // returns num greater than 0 if j bigger than j+1
                if(listArg.get(j).compareTo( listArg.get(j+1)) > 0)
                {
                    swap(j,listArg);

                    swapped = true;
                }
            }
            if(!swapped)
            {
                break;
            }
        }


        return listArg;
    }
}
dawn zephyrBOT
# pastel saffron ``` public class SortingExample<T> { volatile boolean stopSorting = false; /...

Detected code, here are some useful tools:

Formatted code
public class SortingExample<T> {
  volatile boolean stopSorting = false;
  //notifier to tell the sorting thread to stop sorting
  //Returns if we finished sorting or not
  public ArrayList<T> sortList(ArrayList<T> listArg) throws InterruptedException {
    int length = listArg.size();
    boolean swapped = false;
    // false if passes through array without swapping.
    // i is current iteration, j is current index.
    // outter loop (i)
    for (int i = 0; i < length - 1; i++) {
      // checks if we've been signalled to stop sorting, throws interrupt exception if true
      if (stopSorting) {
        return listArg;
      }
      swapped = false;
      // inner loop(j)
      for (int j = 0; j < length - i - 1; j++) {
        if (stopSorting) {
          return listArg;
          //We didn't fully sort
        }
        // notify comparison taking place.
        notifyObservers(this , new SortingEvent(DurationType.FULL_STEP, ActionType.SCANNING, j, (j + 1)));
        // returns num greater than 0 if j bigger than j+1
        if (listArg.get(j).compareTo(listArg.get(j + 1)) > 0) {
          swap(j, listArg);
          swapped = true;
        }
      }
      if (!swapped) {
        break ;
      }
    }
    return listArg;
  }
}
pastel saffron
#
    // returns true if we set stop sorting to true, false if no thread is running.
    public boolean reset(){
        if(runningSortingAlgorithmThread == null){
            return false;
        }
        // first interrupt the thread.
        System.out.println("Stop sorting signal sent to thread");

        stopSorting = true; //notify the thread to stop sorting

        this.copyArray = new ArrayList<Integer>(this.focusArray);
        // must update bar graph gui with new copyArray.
        this.barGraph.setFocusArray(this.copyArray);

        // redraw graph.
        updateGraph();

        return true;
    }


    // commands the sorter to begin sorting the copyArray.
    public void beginSort(){

        // if null or terminated. assign new thread.
        if(runningSortingAlgorithmThread == null ||(runningSortingAlgorithmThread.isAlive()== false)){
            // store method call as an object. Using lambda
            this.runningSortingAlgorithmThread = new Thread(()->
            {   //You can get rid of the interupt flag
                sortingAlgorithm.sortList(copyArray);

            });
        }

        // do nothing if thread is running or null.
        if(runningSortingAlgorithmThread.isAlive()){
            System.out.println("beginSort request rejected.");
            return;
        }
        else{
            // start running it is not alive.
            runningSortingAlgorithmThread.start();
        }
    } ```
#

So something like this

sterile crescent
#

I’ll check this out later today

pastel saffron
#

It'd probably click pretty fast. GL with it

outer girder
outer girder
outer girder
#

you have to prevent 2 threads from modyinf the same thing at the same time

pastel saffron
#

If it's just accessed by one thread or you're never going to not set it to false after you set it to true it doesn't matter

pastel saffron
dry needle
outer girder
dry needle
#

State is changed in the main thread. Other threads read it whenever.

#

AtomicReference is thread safe and it references a read only enum value

#

There's no specify syncronization unlike what you would get with cyclic barrier.

outer girder
dry needle
#

What part of single thread is writing don't you understand.

pastel saffron
#

From what I can tell only one thread will ever write to the flag to reset it, you don't need any synchronization past the inter thread visibility guarantees volatile writes provide

outer girder
#

if reset remove values, and the other thread reads it while it is beig removed, you can end up with reading corrupted info

pastel saffron
ornate arch
#

a more interesting option might be a synchronous queue

#

because the reason you want to stop a thread mid sort is that you want to pause the ui

#

so what if the sorting process just emitted events to the UI via pushing them in a queue?

#

you could "pause" the sort by just not taking the newest event

pastel saffron
dry needle
#

There is also Phaser you could use which would allow deregistering threads as they finish. CyclicBarrier still seems like best approach. You can even add a controller thread that is basically a timer that controls the rate that the rest of the threads advance on. For example, we have 4 threads in that previous example. Make the CyclicBarrier constructor as so new CyclicBarrier(5, collectStepWiseResults) where the fifth thread is your timer/controller thread that calls barrier.await every half second or whatever. The same timer/controller thread can also pause the simulation (don't call barrier.await) or allow step-thru, where barrier.await is only called on button press. The collectStepWiseResults is triggered on each barrier advance (before awakening sorting threads) so you can get their state and swap/compare counts. Unlike phaser, where you can deregister finished threads, a finished sorting thread would still need to just continue on calling barrier.await until that run of all sorts is done.

#

So with a controller thread, you can achieve a UI slider for rate of simulation, and pause, play, and step-thru button functionality.

warped loom
#

I think changing the approach would beenfit here. If you think about it you don't really need parallel sorting, what you need is the UI updated based on sorting sorting steps. So for example your UI will either request a new sort step or not

#

Like Ethan also mentioned

sterile crescent
ornate arch
sterile crescent
ornate arch
#

Java class

sterile crescent
#

Oh

ornate arch
#

like var q = new SynchronousQueue<>()

#

if one thread does q.put(..) it waits for another thread to do q.take()

#

let me make a simple demo

#
void main() throws Exception {
    var stop = new AtomicBoolean();
    var q = new SynchronousQueue<Integer>();
    Thread.startVirtualThread(() -> {
        for (int i = 0; i < 100; i++) {
            try {
                q.put(i);
            } catch (InterruptedException _) {
                break;
            }
        }
        stop.set(true);
    });

    while (!stop.get()) {
        IO.readln();
        IO.println(q.take());
    }
}
sterile crescent
ornate arch
#

but if i ever use the word "design pattern" kill me

sterile crescent
#

Why?

warped loom
#

If you end up with this route you may not need a thread at all, althought its much easier to reason with it

#

I am assuming the UI only needs a rerender on the new "sort event", so if youve broken the algorithm into these states you can just calculate each one at a time

#

But all assumptions on my side tho, I only glanced diagonally

ornate arch
# sterile crescent Why?

because the world of communication that includes "Top Java Design Patterns" I view as dishonest

#

slash anti-intellectual