Java Multithreading Examples

What is multithreading in Java?

A thread is the most lightweight execution environment in Java. Multiple threads can run concurrently within the same process.

A process runs in a self contained environment. It has its own memory address and allocated resources. Most developers consider an application a single process.

Threads run inside a single process and share the same memory space.

Multitasking involves processing multiple tasks in parallel / concurrently / at the same time.

Multitasking by switching processes is costly and time consuming.

Multitasking by switching threads is lightweight and preferred.

What are the 2 ways of multithreading in Java?

There are two ways to create a thread in Java...

1) Extend the Thread class...

public class MyThread extends Thread{
    public void run(){
        System.out.println("This thread is running");
    }
}

Running the thread...

public class Main {
    public static void main(String[] args) {
        Thread myThread = new MyThread();
        myThread.start();
    }
}

2) Implement the Runnable interface...

public class MyThread implements Runnable{
    public void run(){
        System.out.println("This runnable is running");
    }
}

Running the thread...

public class Main {
    public static void main(String[] args) {
        Thread myThread = new Thread(new MyRunnable());
        myThread.start();
    }
}

Both extending the Thread class and implementing the Runnable interface are valid ways of creating threads.

Extending Thread limits you because you can't extend other classes (Java doesn't support multiple inheritance).

Implementing Runnable means you must pass an instance of your class into a new instance of Thread.

What is a good example of multithreading in Java?

Handling concurrent web server requests is a good example of multithreading...

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class MultiThreadedWebServer {
    private static final int PORT = 8080;
    private static final int NUM_THREADS = 4;
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(PORT);
        ExecutorService threadPool = Executors.newFixedThreadPool(NUM_THREADS);
        while (true) {
            // wait for incoming connection
            Socket clientSocket = serverSocket.accept();
            // create a new RequestHandler thread to handle the request
            Runnable requestHandler = new RequestHandler(clientSocket);
            threadPool.execute(requestHandler);
        }
    }

    static class RequestHandler implements Runnable {
        private Socket clientSocket;
        public RequestHandler(Socket clientSocket) {
            this.clientSocket = clientSocket;
        }
        @Override
        public void run() {
            try {
                // handle the request
                // ...
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    clientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

How to avoid deadlock in Java

What is deadlock?

Deadlock occurs when two threads get stuck waiting for each other to release shared resources.

This is is a great example of deadlock...

public class Main {
    public static void main(String[] args) {
        Integer resource1 = 1;
        Integer resource2 = 2;
        Thread thread1 = new Thread() {
            public void run() {
                synchronized (resource1) {
                    System.out.println("Thread 1 locking resource 1");
                    try { Thread.sleep(100);} catch (Exception e) {}
                    synchronized (resource2) {
                        System.out.println("Thread 1 locking resource 2");
                    }
                }
            }
        };
        Thread thread2 = new Thread() {
            public void run() {
                synchronized (resource2) {
                    System.out.println("Thread 2 locking resource 2");
                    try { Thread.sleep(100);} catch (Exception e) {}
                    synchronized (resource1) {
                        System.out.println("Thread 2 locking resource 1");
                    }
                }
            }
        };
        thread1.start();
        thread2.start();
    }
}

Notice how we define two shared Integers resource1 and resource2.

We create two threads. Notice how thread1 first locks resource1 while thread2 first locks resource2.

This results in deadlock because both threads are waiting for the other thread to release the lock.

Remember that the synchronized keyword is what tells a method or block of code to lock the resource so that only ONE thread can access it at a time.

Remember that threads share resources. This is why deadlock occurs.

Avoiding deadlock

1) Don't be a hero

One of the easiest ways to avoid deadlock is to simply not get too fancy. There are many mature libraries and APIs for gracefully handling multithreaded environments in Java. For example, the java.util.concurrent library and the ConcurrentHashMap implementation can help avoid common deadlock mistakes made by developers.

2) Avoid nested locks

The example above uses nested locks. If you need to lock multiple resources, do it in the same order so you don't recreate the example above!

3) Use thread pools

Using thread pools minimizes the chances you will mismanage multiple threads. Our web server example demonstrates the use of the Java Executor framework for efficiently managing a collection of threads.

4) Use timeouts

Establishing timeouts around synchronized code blocks helps ensure deadlock won't happen. If an execution path isn't able to lock a resource then it can eventually move on instead of waiting for the resource to be unlocked indefinitely (deadlock).

How many threads can run at a time in Java?

In short, the limit does not exist :).

The number of threads a program can run is dependent on the underlying resources the application has to work with.

You could theoretically support an infinite number of threads based on the operating system, JVM implementation, CPU, and memory available to the environment in which your application is running.

Your thoughts?