Zhyhalo Oleksandr
Zhyhalo Oleksandr

Reputation: 33

Java multithreading task

I've got task like this

Build class StringTask, simulating long-term calculations, here consisting of the concatenation of strings.

The class constructor receives as argument the string to duplicate, and the number indicating how many times the string is to be amplified.

Class must implement the Runnable interface, and in its run() method is performed duplicating of a string, it should be done using + used to string variables (it is long-term operation). Usage of the + is a mandatory condition.

The object of class StringTask is treated as a task that can be performed in parallel with each other.

The status tasks are:

Define the following methods in class StringTask:

The following program code:

public class Main {

    public static void main(String[] args) throws InterruptedException {
        StringTask task = new StringTask("A", 70000);
        System.out.println("Task " + task.getState());
        task.start();
        if (args.length > 0 && args[0].equals("abort")) { 
        /*
            <- here add the code interrupting the task after a second 
            and run it in a separate thread
        */
        }
        while (!task.isDone()) {
            Thread.sleep(500);
            switch(task.getState()) {
                case RUNNING: System.out.print("R."); break;
                case ABORTED: System.out.println(" ... aborted."); break;
                case READY: System.out.println(" ... ready."); break;
                default: System.out.println("uknown state");
            }
        }
        System.out.println("Task " + task.getState());
        System.out.println(task.getResult().length());
    }
}

run without an argument should put something like this:

Task CREATED R.R.R.R.R.R.R.R.R. ... ready. Task READY 70000 and run with argument abort put something like this:

Task CREATED R. ... aborted. Task ABORTED 31700 Note 1. Main.java file can be modified only in the place marked /* <- */ Note 2. Do not use the method System.exit(...)

And got this solution

package zad2;

/**
 * Created by szhyhalo on 09.04.17.
 */
public class StringTask implements Runnable {


    public enum TaskState {
        CREATED, RUNNING, ABORTED, READY
    }

    public boolean isDone = false;
    public TaskState myState;
    public String text;
    public String resultText;
    public Thread myThread;
    public int operations;

    public StringTask() {
        myState = TaskState.CREATED;
    }

    public StringTask(String a, int i) {
        this();
        text = a;
        operations = i;
        resultText = "";
    }

    public boolean isDone() {
        if (myState == TaskState.READY || myState == TaskState.ABORTED) return true;
        return false;
    }

    public void abort() {
        while (operations != 0) {
            resultText += text;
            operations--;
        }
    }

    @Override
    public void run() {
        while (operations != 0) {
            resultText += text;
            operations--;
        }
        myState = TaskState.READY;
    }

    public TaskState getState() {
        return myState;

    }

    public String getResult() {
        return resultText;
    }

    public void start() {
        myThread = new Thread(this);
        myState = TaskState.RUNNING;
        myThread.start();


    }
}

But after testing, the system give me these errors, and i dont know hove to solve them.

Behaviour tests results - scoring decrease = -7

WHEN: We are invoking StringTask constructor with arguments 'Y' i 80000, and then we are staring the task


Requirements tests results - scoring decrease = -2

Case 1: scoring decrease = -1 - Some variables should have have volatile modifier

Case 2: scoring decrease = -1 - You should have check interrupted flag by interrupted lub isInterrupted method

How to solve this? I tried, but I have no idea.

Upvotes: 2

Views: 609

Answers (1)

Tamas Rev
Tamas Rev

Reputation: 7166

Since neither of us knows the actual failing Behaviour tests, all we can do is guessing.

First thing first, in StringTask you should use private volatile variables. They should be private for the sake of encapsulation. They should be volatile so the multi-threaded changes will be visible. See this SO Q&A for further discussion about volatile.

Regarding the operations counter, you better use AtomicInteger because that supports multithreaded decrement and increment operations.

So these are the refined fields:

private volatile TaskState myState;
private volatile String text;
private volatile String resultText;
private final AtomicInteger operations = new AtomicInteger(0);

This requires a small change in the second constructor:

public StringTask(String a, int i) {
    this();
    text = a;
    operations.set(i); // this is the change
    resultText = "";
}

Also, the abort() method needs to stop the execution in run(). I'd do it like, abort() updates the myState flag, and run() keeps reading that flag:

public void abort() {
    myState = TaskState.ABORTED;
}

@Override
public void run() {
    myState = TaskState.RUNNING;
    while (operations.get() != 0 && myState != TaskState.ABORTED) {
        resultText += text;
        operations.decrementAndGet();
    }
    if (myState != TaskState.ABORTED) {
        myState = TaskState.READY;
    }
}

Note that these methods above use the get() and the decrementAndGet() method of AtomicInteger(). This is a slight inconvenience we must pay for the very safe decrement / increment operations.

Upvotes: 1

Related Questions