Basic use of Java thread

Keywords: Java

Create thread

There are two ways to create threads:

  • Inherit Thread class
  • Implement the Runnable interface

The Thread class implements the Runnable interface. The biggest limitation of creating threads by inheriting Thread classes is that multiple inheritance is not supported. So in order to support multiple inheritance, the Runnable interface should be implemented. The threads created in the two ways are the same at work, and there is no essential difference.

The first way is to inherit the Thread class and override the run() method:

public class Work extends Thread {
    
    @Override
    public void run() {
        System.out.println("Working...");
    }
}

public class Run {
    public static void main(String[] args) {
        Work work = new Work();
        work.start();
        System.out.println("End!");
    }
}

Run result may be "End!" Output first. When multithreading is used, the run result is independent of the call order.

Calling the run() method is just a normal method call and does not start the thread. If the start() method is called more than once, an IllegalThreadStateException is thrown.

The second way is to implement the Runnable interface:

public class Work implements Runnable {
    
    @Override
    public void run() {
        System.out.println("Working...");
    }
}

public class Run {
    public static void main(String[] args) {
        Thread t = new Thread(new Work());
        t.start();
        System.out.println("End!");
    }
}

This way is no different from the first one in operation. Its advantage lies in breaking through the limitation of single inheritance.

Some construction methods of Thread class:

Construction method Explain
Thread() Create a new thread
Thread(String name) Create a new thread and specify a name
Thread(Runnable target) Create a new thread with target as the running object
Thread(Runnable target, String name) Take target as the running object and specify the name
Thread(ThreadGroup group, Runnable target) target as run object and as a member of thread group

Thread method

currentThread() method

The currentThread() method returns information about the thread being executed.

public class Run() {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
    }
}

The above code outputs "main" in the console, indicating that the method is called by a thread named main.

import static java.lang.System.out;

public class Run {

    static class Work extends Thread {
    
        @Override
        public void run() {
            out.printf("%s Be called\n", currentThread().getName());
        }
    }

    public static void main(String[] args) {
        Work t1 = new Work(),
                t2 = new Work();
        t1.start();
        t2.start();
    }
}

Running results of the above code:

Thread-0 Be called
Thread-1 Be called

Process finished with exit code 0

In the run() method, you can omit the Thread and call the currentThread() method directly.

isAlive() method

This method determines whether the current thread is active.

import static java.lang.System.out;

public class Run {

    static class Work extends Thread {
    
        @Override
        public void run() {
            out.printf("In operation %s\n", isAlive());
        }
    }

    public static void main(String[] args) throws Throwable {
        Work t = new Work();
        out.printf("Before running: %s\n", t.isAlive());
        t.start();
        // Wait for the thread to finish running
        Thread.sleep(1000);
        out.printf("End of operation: %s\n", t.isAlive());
    }
}

Running results of the above code:

Before running: false
 Running true
 End of run: false

Process finished with exit code 0

sleep() method

The sleep() method specifies the number of milliseconds for the current thread to sleep (pause), which does not release the lock.

Stop thread

interrupt() method

The interrupt() method does not stop the thread immediately, but marks a stop in the thread.

import static java.lang.System.out;

public class StopThread {

    static class Work extends Thread {

        @Override
        public void run() {
            for (int i = 1; i <= 50000; i++) {
                out.printf("i = %d\n", i);
            }
        }
    }

    public static void main(String[] args) throws Throwable {
        Work work = new Work();
        work.start();
        Thread.sleep(200);
        work.interrupt();
        out.println("Call interrupt!");
    }
}

Running results of the above code:

...
i = 8190
i = 8191
i = 8192
Call interrupt!
i = 8193
i = 8194
i = 8195
...

After the interrupt() method call, the thread is still running.

To stop a thread using the interrupt() method, you need to determine the interrupt status in the thread. There are two methods:

  • interrupted(): test whether the current thread is in an interrupted state. After execution, clear the state to false.
  • isInterrupted(): the function is the same as above, but the status is not cleared.
import static java.lang.System.out;

public class StopThread {

    static class Work extends Thread {

        @Override
        public void run() {
            for (int i = 1; i <= 50000; i++) {
                if (isInterrupted()) {
                    out.println("Jump out of circulation!");
                    break;
                }
                out.printf("i = %d\n", i);
            }
        }
    }

    public static void main(String[] args) throws Throwable {
        Work work = new Work();
        work.start();
        Thread.sleep(200);
        work.interrupt();
        out.println("Call interrupt!");
    }
}

Execution result of the above code:

...
i = 8301
i = 8302
i = 8303
i = 8304
i = 8305
i = 8306
i = 8307
Call interrupt!
//Out of the loop!

Process finished with exit code 0

After calling the interrupt() method, the loop has exited. But this method just jumps out of the loop. If there is code outside the for loop, it will still execute.

Throw an exception to stop the thread

When the thread status is judged to be interrupted, an exception can be thrown and the post interrupt processing can be performed in the catch or finally block:

import static java.lang.System.out;

public class StopThread {

    static class Work extends Thread {

        @Override
        public void run() {
            try {
                for (int i = 1; i <= 50000; i++) {
                    if (isInterrupted()) {
                        out.println("Interrupted!");
                        throw new InterruptedException("Throw an exception!");
                    }
                    out.printf("i = %d\n", i);
                }
                out.println("for Loop end!");
            } catch (InterruptedException e) {
                out.println(e.getMessage());
            }
        }
    }

    public static void main(String[] args) throws Throwable {
        Work work = new Work();
        work.start();
        Thread.sleep(200);
        work.interrupt();
        out.println("Call interrupt!");
    }
}

The above code puts the task to be executed by the thread into the try block. When it is judged to be in the interrupt state, the InterruptedException is thrown. If the lock needs to be released, it can be executed in the finally block.

You can also cooperate with return to stop the thread:

if (isInterrupted()) {
    return;
}

Pause thread

Java provides suspend() and resume() methods to pause and resume threads, but these two methods are expired and obsolete.

When the suspend() method pauses a thread, the lock is not released. So using the suspend() method is prone to deadlock.

If you need to pause the thread, you can add a tag. If the tag indicates that the thread needs to pause, use wait() to enter the waiting state. If you need to resume, use notify() to wake up.

import static java.lang.System.out;

public class StopThread {

    static class Work extends Thread {
        // Pause mark
        private boolean isSuspended = false;

        void pause() {
            isSuspended = true;
        }

        synchronized void wake() {
            isSuspended = false;
            // awaken
            this.notify();
            out.println("Awaken!");
        }

        @Override
        public void run() {
            synchronized (this) {
                try {
                    for (int i = 1; i <= 5000; i++) {
                        if (isInterrupted()) {
                            return;
                        }

                        if (isSuspended) {
                            out.println("Suspended!");
                            // wait for
                            this.wait();
                        }

                        out.printf("%s i = %d\n", getName(), i);
                    }
                    out.printf("%s end!\n", getName());
                } catch (InterruptedException e) {
                    out.println(e.getMessage());
                }
            }
        }
    }

    public static void main(String[] args) throws Throwable {
        Work work = new Work();
        work.start();
        Thread.sleep(100);
        // suspend
        work.pause();
        Thread.sleep(100);
        // awaken
        work.wake();
    }
}

The above code uses wait() and notify() to suspend and resume threads. Operation result:

...
Thread-0 i = 202
Thread-0 i = 203
Thread-0 i = 204
//Suspended!
//Wake up!
Thread-0 i = 205
Thread-0 i = 206
Thread-0 i = 207
...
Thread-0 i = 4998
Thread-0 i = 4999
Thread-0 i = 5000
Thread-0 end!

Process finished with exit code 0

yield method

The yield() method is used to give up the current CPU resources and let other tasks take up CPU execution time. But the time of giving up is uncertain. It is possible to just give up and get time slice right away.

import static java.lang.System.currentTimeMillis;
import static java.lang.System.out;

public class Yield {
    static class Work extends Thread {

        @Override
        public void run() {
            long before = currentTimeMillis();
            int sum = 0;
            for (int i =1; i < 2000000; i++) {
                // yield();
                sum += (i + 1);
            }
            long after = currentTimeMillis();
            out.printf("Cost: %dms\n", after - before);
        }
    }

    public static void main(String[] args) {
        new Work().start();
    }
}

When the above code does not use the yield() method, it will be executed in about 15 ms, plus about 500 ms.

Posted by lcoscare on Mon, 21 Oct 2019 22:41:12 -0700