Daniel Zawadzki
Daniel Zawadzki

Reputation: 179

Concurrently working tasks

Im trying to prepare simple program, which allows user to create few concurrently working tasks. Nothing special. Every tasks start from value 1 and add another 1 to the moment when it reachs maximum (every task got different max value). Then task inform, that the designated value has been reached and it stops. All tasks must be contained in ArrayList, what i made. I need to provide methods, which allows (in every moment)

  1. Check status of the task
  2. Check result of the task
  3. End the task individually, or all in one
  4. Display list, which contains all tasks (Name, Value, Status)

I would prefer to avoid gui, as it's not required by my professor. I already made part, but i don't know how to print all elements of list (TASK_LIST) and stop individual tasks.

MAIN.java

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {

  public static void main(String[] args) {

    ExecutorService exec = Executors.newCachedThreadPool();

    Task t1 = new Task("Task 1", 2);
    Task t2 = new Task("Task 2", 20);
    Task t3 = new Task("Task 3", 5);

    exec.execute(t1);
    exec.execute(t2);
    exec.execute(t3);

    //showTASK_LIST();

  }
}

TASK.java

    import java.util.ArrayList;
    import java.util.List;

    class Task implements Runnable {
        String TASK_NAME;
        int TASK_RESULT;
        int TASK_GOAL;
        String TASK_STATUS;
        static List<Task> TASK_LIST = new ArrayList<Task>();

        public Task(String name, int goal) {
            this.TASK_NAME = name;
            this.TASK_GOAL = goal;
            this.TASK_STATUS = "INACTIVE";
            TASK_LIST.add(this);
        }

        public void run() {
            TASK_STATUS="ACTIVE";
            System.out.println(TASK_NAME + " starts.");
            while (TASK_RESULT != TASK_GOAL){
                //if (Thread.currentThread().isInterrupted()) return;
                TASK_RESULT++;
                System.out.println(TASK_NAME + " " + TASK_RESULT);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Thread.yield();

            }
            System.out.println("===========================================\n" + 
                                TASK_NAME + " has been completed. Final result = " + TASK_GOAL +
                                "\n===========================================" );
            setTASK_STATUS("COMPLETED");
            System.out.println(TASK_LIST.size());


        }

        //Method to check current result
        public int getTASK_RESULT() {
            return TASK_RESULT;
        }

        //Method to check current status
        public String getTASK_STATUS() {
            return TASK_STATUS;
        }

        //Method to change result
        public void setTASK_STATUS(String status) {
            TASK_STATUS = status;
        }

        //This part doesnt work
        public showTASK_LIST(){
            for(int i = 0; i <= TASK_LIST.size(); i++){
                System.out.println(TASK_LIST.get(i).TASK_NAME);
            }

        }

    }

This is how it looks at this moment.

Upvotes: 0

Views: 126

Answers (2)

Elia Rohana
Elia Rohana

Reputation: 326

you have Thread safety problems with your code since you are trying to read/write values on a shared resource (Task object in this case) from multiple threads :

you are changing the task status(write) from one thread and tries to read it from different thread(read) which is the Main thread is this case:

Task.showTASK_LIST()

same problem also with:

      //Method to check current result
    public int getTASK_RESULT() {
        return TASK_RESULT;
    }

    //Method to check current status
    public String getTASK_STATUS() {
        return TASK_STATUS;
    }

you can read about Java concurrency and Thread safety at: https://en.wikipedia.org/wiki/Thread_safety https://docs.oracle.com/javase/tutorial/essential/concurrency/

i will provide my own implementation while reusing you code, but before that few keynotes before you look into the code:

  1. use Callable interface instead of Runnable since you want to return a result at the end of the processing(Callable is similar to Runnable, but you can return a result
  2. use Executor submit() method which returns Future object(Future has many useful methods like : isDone(), cancel(), get(), etc...)
  3. manage you list of tasks in the Main thread, and outside your Task class

StatusChangeListener

public interface StatusChangeListener {

  void statusChanged(String oldStatus, String newStatus);
}

Main

import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main {

  public static void main(String[] args) {

    ExecutorService executor = Executors.newCachedThreadPool();

    //manage your tasks list here
    ArrayList<Task> tasks = new ArrayList<>();
    Task t1 = new Task("Task 1", 2);
    Task t2 = new Task("Task 2", 20);
    Task t3 = new Task("Task 3", 5);

    tasks.add(t1);
    tasks.add(t2);
    tasks.add(t3);

    //add status change listener to be notified for t1 status changes
    t1.addStatusChangeListener(new MyListener());

    System.out.println("tasks = " + tasks);

    //use submit instead of execute
    Future<Integer> future1 = executor.submit(t1);

    System.out.println("task 1 is done = " + future1.isDone());

    Future<Integer> future2 = executor.submit(t2);
    Future<Integer> future3 = executor.submit(t3);
    //cancel task 3
    future3.cancel(true);

    try {
      System.out.println("future 1 result= " + future1.get());
      System.out.println("future 2 result= " + future2.get());
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }
  }

  //you can do the same with the progress. e.g. add progress listener
  private static class MyListener implements StatusChangeListener{

    @Override
    public void statusChanged(String oldStatus, String newStatus) {
      System.out.println(String.format("Thread : %s status changed from %s to %s", Thread.currentThread(), oldStatus, newStatus));
    }
  }

}

Task

import java.util.concurrent.Callable;

class Task implements Callable<Integer> {

  //can be replaced by enum
  private String TASK_STATUS;
  private String TASK_NAME;
  private int TASK_RESULT;
  private int TASK_GOAL;
  private StatusChangeListener statusChangeListener;


  public Task(String name, int goal) {
    this.TASK_NAME = name;
    this.TASK_GOAL = goal;
    this.TASK_STATUS = "INACTIVE";
  }

  //private Method to change result
  private void setTASK_STATUS(String newStatus) {
    String oldStatus = TASK_STATUS;
    TASK_STATUS = newStatus;
    //notify status changes
    if (statusChangeListener != null)
      statusChangeListener.statusChanged(oldStatus, newStatus);
  }

  @Override
  public Integer call() throws Exception {
    setTASK_STATUS("ACTIVE");
    System.out.println(TASK_NAME + " starts.");
    while (TASK_RESULT != TASK_GOAL) {
      //if (Thread.currentThread().isInterrupted()) return;
      TASK_RESULT++;
      System.out.println(TASK_NAME + " " + TASK_RESULT);
      try {
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      Thread.yield();

    }
    System.out.println("===========================================\n" +
            TASK_NAME + " has been completed. Final result = " + TASK_GOAL +
            "\n===========================================");
    setTASK_STATUS("COMPLETED");
    return TASK_RESULT;
  }

  public void addStatusChangeListener(StatusChangeListener statusChangeListener) {
    this.statusChangeListener = statusChangeListener;
  }
}

Upvotes: 1

sbowde4
sbowde4

Reputation: 777

You need to make your showTASK_LIST() static. Then you can call it with Task.showTASK_LIST();

Like below

   public static void showTASK_LIST(){
        for(int i = 0; i <= TASK_LIST.size(); i++){
            System.out.println(TASK_LIST.get(i).TASK_NAME);
        }

Task.showTASK_LIST();//goes in your main method to show the tasks

The result I got when I ran your code produced an indexOutOfBounds exception in the above code. It is a simple fix.

Change your for loop to this

for(int i = 0; i < TASK_LIST.size(); i++)// changed <= to <

There are only 3 tasks, <= goes up to a fourth task causing problems

I got this output

Task 1 starts.
Task 3 starts.
Task 3 1
Task 1
Task 2
Task 3
Task 2 starts.
Task 1 1
Task 2 1
Task 3 2
Task 1 2
Task 2 2
Task 3 3
===========================================
Task 1 has been completed. Final result = 2
===========================================
Task 2 3
3
Task 3 4
Task 2 4
Task 2 5
Task 3 5
Task 2 6
===========================================
Task 3 has been completed. Final result = 5
===========================================
3
Task 2 7
Task 2 8
Task 2 9
Task 2 10
Task 2 11
Task 2 12
Task 2 13
Task 2 14
Task 2 15
Task 2 16
Task 2 17
Task 2 18
Task 2 19
Task 2 20
===========================================
Task 2 has been completed. Final result = 20
===========================================
3

Upvotes: 1

Related Questions