1

This question continues this question.

I am trying to stop thread that inserts rows to dabase, but I always got an exception

This part of code shows my thread's logic

public void run() {
        String number = "";
        try {
            while (!Thread.currentThread().isInterrupted()) {
                number = generateNumber();
                database.insertOrUpdateRow(number);
            }
        } catch (SQLException ex) {
            LOGGER.error("exception while inserting number " + number + " " + ex.getMessage());
        }
    }

I have googled how to stop thread correctly and found that I should just flag that thread is stopped. So, this is what I got

public boolean stop() {
        try {
            Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
            for (Thread t : threadSet) {
                if (t != null) {
                    if (t.getName().contains("generate")) {
                        t.interrupt();
                        LOGGER.info(t.getName()+" is stopped");
                    }
                }
            }
        } catch (Exception e) {
            LOGGER.error("Exception when call stop: " + e.toString());
        }
        return true;
    }

My stacktrace

[INFO] [qtp1804418913-32] INFO se.homework.hwbs.tasks.un.server.NumberQtyService
Impl - generate37 is stopped
[INFO] [generate37] ERROR se.homework.hwbs.tasks.un.server.threads.InsertRowThre
ad - exception while inserting number 34587 transaction rollback: serialization
failure
[INFO] [qtp1804418913-33] ERROR se.homework.hwbs.tasks.un.server.database.Databa
se - error select cnt by numberjava.sql.SQLTransactionRollbackException: transac
tion rollback: serialization failure
[INFO] [qtp1804418913-34] ERROR se.homework.hwbs.tasks.un.server.database.Databa
se - error select cnt by numberjava.sql.SQLTransactionRollbackException: transac
tion rollback: serialization failure

Thread stops, everything works, but this exceptions make me feel bad. Why are they happens? How to stop thread when I work with database in correct way? Database is HsqlDB.

8
  • 1
    Seems that on that thread methods are being called that react to interruption with an abort of operation and rollback. So your way to go may be introducing a Boolean flag - let's call it "bQuit" that you can check instead of interrupted state. May I ask why you sort out the Thread that complicated instead of keeping a reference on creation? Commented Dec 9, 2015 at 12:18
  • You read all about why stopping thread is not the best of ideas? Do you have a loop inside the thread you want to end? Commented Dec 9, 2015 at 12:19
  • Another approach would be to have "generateNumber" generate a "Poison" Value that you can check and exit the loop if it is encountered. But this depends on the assumption, that there is at least 1 value that can never be a legal one for "normal" processing. So if you are only allowed to insert positive values, you could use "-1" as poison. Commented Dec 9, 2015 at 12:24
  • @Fildor I assume that I could have several threads. So, I am trying to stop all of them. You gave me idea that I could just create List<Thread> and put threads there and then stop them. I wil try, but unfortunately it won't solve my problem. Commented Dec 9, 2015 at 12:25
  • @Flidor good idea, but looks like hack, not best way, don't you think so? Commented Dec 9, 2015 at 12:27

2 Answers 2

1

Instead of enumerating threads and selecting one by name, you might consider using a stop variable in your insert thread which can be directly controlled.

Consider this GeneratorThread:

public class GeneratorThread  extends Thread {

    protected static volatile boolean stop;

    public static void stopExecution() {
        stop = true;
    }

    public GeneratorThread() {
        stop = false;
    }

    public void run() {
        String number = "";
        try {
            while (!stop) {
                number = generateNumber();
                database.insertOrUpdateRow(number);
            }
        } catch (SQLException ex) {
            //Any SQLException will terminate your thread. Is this wanted?!
            LOGGER.error("exception while inserting number " + number + " " + ex.getMessage());
        }
    }
}

You can stop this by calling GeneratorThread.stopExecution() which will set the flag to false causing the current number to be the last inserted.

Of cause this implies that there will be only one GeneratorThread ever in your application.

Just as well, you might rethink your while and try-catch positions in run. This thread would end on any SQLException whatsoever. If you want to keep the thread running even if there are (temporary?) errors, you might want to do

    while (!stop) {
        String number = "";
        try {
            number = generateNumber();
            database.insertOrUpdateRow(number);
        } catch (SQLException ex) {
            LOGGER.error("exception while inserting number " + number + " " + ex.getMessage());
        }
    }
Sign up to request clarification or add additional context in comments.

Comments

0

I combined ideas of @Jan and @Fildor.

So, this is what I got

public class GeneratingThread extends Thread {

private final Logger LOGGER = LoggerFactory.getLogger(InsertRowThread.class);
private final Database database;
private boolean stop = false;

public GeneratingThread(Database database) {
    this.database = database;
}

@Override
public void run() {
    String number = "";
    try {
        while (!stop) {
            number = generateNumber();
            database.insertOrUpdateRow(number);
        }
    } catch (SQLException ex) {
        LOGGER.error("exception while inserting number " + number + " " + ex.getMessage());
    }
}

public void stopThread() {
    this.stop = true;
}

private String generateNumber() {
    int number = ThreadLocalRandom.current().nextInt(0, 100000);
    String sb = Integer.toString(number);
    while (sb.length() < 5) {
        sb = "0" + sb;
    }
    return sb;
}
}

And this is my starting of thread

private final List<GeneratingThread> generatingThreads = new ArrayList<>();
GeneratingThread thread = new GeneratingThread(db);
thread.start();
generatingThreads.add(thread);

And stop it

for (GeneratingThread gt : generatingThreads) {
        if (gt != null) {
            gt.stopThread();
            LOGGER.info("Thread is stopped");
        }
    }

Worked perfect for me. Thanks for discussion!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.