DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Designing a Java Connector for Software Integrations
  • How to Convert XLS to XLSX in Java
  • Recurrent Workflows With Cloud Native Dapr Jobs
  • Java Virtual Threads and Scaling

Trending

  • Concourse CI/CD Pipeline: Webhook Triggers
  • Hybrid Cloud vs Multi-Cloud: Choosing the Right Strategy for AI Scalability and Security
  • AI’s Role in Everyday Development
  • Streamlining Event Data in Event-Driven Ansible
  1. DZone
  2. Coding
  3. Java
  4. Java Concurrency, Part 2: Manipulating Threads

Java Concurrency, Part 2: Manipulating Threads

Learn more about manipulating threads in your Java projects.

By 
Baptiste Wicht user avatar
Baptiste Wicht
·
Oct. 16, 19 · Tutorial
Likes (9)
Comment
Save
Tweet
Share
19.0K Views

Join the DZone community and get the full member experience.

Join For Free

Image title

After seeing how to create Threads, this article will explore what we can do to manipulate Threads in our Java applications. 

Please keep in mind all code demonstrated in this article has been tested and debugged in Java 12. 

So without further ado, let's get started.

You may also like:  A Java 8 Streams Cookbook

When we have Threads, we have several operations:

  • Make the current Thread  sleeping during x milliseconds
  • Wait for another thread to complete
  • Manage the priorities of  Threads and pause a thread to give another thread the opportunity to run
  • Interrupt a thread

We'll now see how to do all these things.

First and easier, we can make a thread sleeping for a certain number of milliseconds. To do that, the Thread class has a method of sleep (long millis). But this method is static, so you can only make the current Thread sleeping. You cannot choose the Thread you want to make sleeping, your only choice is the current Thread, so we have:

Thread.sleep(1000);


This makes the current Thread sleep during 1000 milliseconds (1 second). But, you have to catch an exception, InterruptedException. This exception occurs if the sleeping thread is interrupted. So you have to do that:

try {
    Thread.sleep(1000);
} catch (InterruptedException e){
    e.printStackTrace();
}


But this is not a good way to manage the InterruptedException. We'll see in one moment how to deal with this exception.

If you want more precision, you can use the overloaded version of sleep that takes the number of milliseconds plus a certain number of nanoseconds to sleep. The precision of this sleep depends on the system timers and clocks.

For example, if you want to sleep 1000 milliseconds and 1000 nanoseconds (1 microsecond), you can do so like this:

try {
    Thread.sleep(1000, 1000);
} catch (InterruptedException e){
    e.printStackTrace();
}


Here is a little example to test that:

public class SleepThread {
    public static void main(String[] args) {
        System.out.println("Current time millis : " + System.currentTimeMillis());

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Current time millis : " + System.currentTimeMillis());

        System.out.println("Nano time : " + System.nanoTime());

        try {
            Thread.sleep(2, 5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Nano time : " + System.nanoTime());
    }
}


In my computer, this produces the following result:

Current time millis : 1273959308480
Current time millis : 1273959309480
Nano time : 5878165216075
Nano time : 5878166730976


You can see that the sleep of milliseconds is very precise, but with nanoseconds, the result can vary a lot. And of course, the result depends of your computer, your operating system, and your configuration.

Another thing you can do with  Threads is wait for another thread to die. For example, you can create five thread to compute the sub result and wait for these 5 threads to finish to compute the final results based on the results of the five threads. To do that, you can use the join() method of the Thread class. This method is not static, so you can use it on any thread to wait for it to die. Like  sleep(), this method throws InterruptedException when the thread is interrupted waiting for another thread. So to wait on thread2, you just have to do that:

try {
    thread2.join();
} catch (InterruptedException e){
    e.printStackTrace();
}


That will make the current Thread waiting for thread2 die. You can also add a timeout in millis, or millis + nanos, with the overloaded versions of join(), join(long millis)  and  join(long millis, int nanos). Here is a little example that shows all of that stuff:

public class JoinThread {
    public static void main(String[] args) {
        Thread thread2 = new Thread(new WaitRunnable());
        Thread thread3 = new Thread(new WaitRunnable());

        System.out.println("Current time millis : " + System.currentTimeMillis());

        thread2.start();

        try {
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Current time millis : " + System.currentTimeMillis());

        thread3.start();

        try {
            thread3.join(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Current time millis : " + System.currentTimeMillis());
    }

    private static class WaitRunnable implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        }
}


This will produce the following result on my computer:

Current time millis : 1274015478535
Current time millis : 1274015483538
Current time millis : 1274015484538


You can see that the first join() wait 5 seconds for the other thread, and when we set a timeout, we wait only 1 second and return from the join method.

When working with  Threads, it's also possible to change the priority of a Thread. In the Java Virtual Machine, the Thread scheduler, use priority-based scheduling. So if a Thread entered in Runnable state with a higher priority than the running Thread, the new Thread will run and the current running thread will return to Runnable state and waits for its turn. But this behavior is not guaranteed and is completely depending on the virtual machine you are working on. So, do not rely on thread priorities, just use them to improve the efficiency of your program.

Normally, the priority range of Threads is an integer from 0 to 10, but some virtual machines have lower or higher ranges. To know the range of priority, you can use constants of the Thread class :

public class ThreadPriorityRange {
    public static void main(String[] args) {
        System.out.println("Minimal priority : " + Thread.MIN_PRIORITY);
        System.out.println("Maximal priority : " + Thread.MAX_PRIORITY);
        System.out.println("Norm priority : " + Thread.NORM_PRIORITY);
         }
}


On my computer, I have the most current values:

Minimal priority : 1
Maximal priority : 10
Norm priority : 5


To set the priority of a Thread, you can use the setPriority(int priority)  method of the Thread class. If you enter a value greater than the maximal priority, the maximal value will be used. If you don't specify a priority, the used priority will be the priority of the current Thread.

Another way to works with priority is the  yield() method. This method is static, so this works on the current Thread. The purpose of this method is to make the Thread going to Runnable again and to give the opportunity to other threads to get their turn. But in practice, the behavior of this method is not guaranteed. It can be implemented as a no-op on certain systems. It's not easy to test that in practice, because the results can truly depend on your computer, virtual machine, and operating system. It's a good thing to not use the priorities of  Thread =s in practice.

The last thing you can do with a Thread is to interrupt it. In Java, you have no way to force a Thread to stop, if the Thread is not well-done, it can continue its execution infinitely. But you can interrupt a Thread with the interrupt()  method. This method interrupts the thread; if the thread is sleeping or joining another Thread, an InterruptedException is thrown. You have to know that if the thread was sleeping or joining, the interrupted status of the Thread will be cleared. Namely, the method isInterrupted()  will return false. A little example to demonstrate that is shown below:

public class InterruptThread {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new WaitRunnable());

        thread1.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread1.interrupt();
    }

    private static class WaitRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("Current time millis : " + System.currentTimeMillis());

            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                System.out.println("The thread has been interrupted");
                System.out.println("The thread is interrupted : " + Thread.currentThread().isInterrupted());
            }

            System.out.println("Current time millis : " + System.currentTimeMillis());
        }
        }
}


That will produce this kind of result:

Current time millis : 1274017633151

The thread has been interrupted

The thread is interrupted : false

Current time millis : 1274017634151


You can see that after one second, the second thread is interrupted and that the interrupted status has been set to false. If you are not sleeping, but making a lot of heavy actions, you can test for interrupt like that to make your thread correctly interrupts:

public class InterruptableRunnable implements Runnable {
    @Override
    public void run() {
        while(!Thread.currentThread().isInterrupted()){
            //Heavy operation
        }
    }
}


Now that you know how to interrupt a thread, you can imagine simplint caching the InterruptedException is not enough to make your thread "interrupt safe". Imagine that your thread has something like this:

public class UglyRunnable implements Runnable {
    @Override
    public void run() {
        while(!Thread.currentThread().isInterrupted()){
            //Heavy operation
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //Other operation
        }
    }
}


And now, another Thread want to interrupt your thread while your thread is sleeping. The sleep will be interrupted, but the interrupted status will be cleared so the loop will continue. A solution to make a better thread is to interrupt again the thread after an InterruptedException:

public class BetterRunnable implements Runnable {
    @Override
    public void run() {
        while(!Thread.currentThread().isInterrupted()){
            //Heavy operation
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            //Other operation
        }
    }
}


With that code, the interrupted status will be restored and the loop will be stopped after interrupt. Depending on your code, you can also add a continue statement after the interrupt()  to not make operations after interrupt. In some cases, you'll also need to make several if statements to test the interrupted status to do or not to do some operations. Keep in mind that 

So, we've now covered the main operations you can do on threads. I hope you found this article interesting!

You can download the sources of this article here: Java Concurrency Sources - Part 2

In the next article on Java concurrency, we'll see how to synchronize code to make it Thread-safe.

Stay tuned!

Further Reading

Intricacies of Multi-Threading in Java

A Java 8 Streams Cookbook

Getting the Most Out of the Java Thread Pool

Java (programming language)

Published at DZone with permission of Baptiste Wicht, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Designing a Java Connector for Software Integrations
  • How to Convert XLS to XLSX in Java
  • Recurrent Workflows With Cloud Native Dapr Jobs
  • Java Virtual Threads and Scaling

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: