Java Concurrency: Threads and Runnables

Threads are everywhere, they are the basic building block of every server application out there.

Usually in Java using threads is just a combination of Executors and Runnables however let’s have a closer look on a thread and how it works.

 

Supposing I want to start a thread, it can be as simple as this.

        Thread thread = new Thread(() {
            System.out.println("Running somewhere else");
        });
        thread.start();
        thread.join();

What will happen in this case is that we create a thread and we join the thread until the operation finishes.

If we check the internals of the thread class we can see that in our initialization we passed a Runnable.

So the Runnable essentially instructs the thread what to do. It encapsulates the logic that will be executed once we start a thread.
If we check the source code the Runnable we can see that it is an interface with only one function.

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

Essentially Runnable is a functional interface.

From the documentation.

This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

The runnable can be passed to lambdas, Executors-Thread pools and use it to create standalone threads.

So let’s have a closer look on what a thread does.

If we check the implementation we can see that a Thread implements the Runnable interface.

public class Thread implements Runnable {
...
    public Thread(Runnable target) {
        this(null, target, "Thread-" + nextThreadNum(), 0);
    }
...
}

We also displayed a specific thread constructor on purpose, the constructor passes the runnable as a target variable.

If we check the run method of the thread it will execute the Runnable we passed previously.


...
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

Supposing no runnable has been passed in one of the constructors the thread will not execute anything.
So let’s see another approach where we shall run a thread by extending it.

public class CustomThread extends Thread {

    @Override
    public void run() {
        System.out.println("No runnable passed");
    }

}

Which we can run like this

        Thread thread = new CustomThread();
        thread.start();
        thread.join();

So start will essentially execute the run the method that we implemented or the original run method using the target Runnable.
If we check start we shall stumble upon the following code block

...
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
...

As we see there a method called start0() is invoked.
This method is essentially

    private native void start0();

We can see the native methods on the  Thread.c class.

static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
    ...
    {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};

This brings us to the actual native code starting the thread which will end up on the actual implementation based on the os used.
For example on linux it would be pthreads.

Advertisement

Java and dynamic Proxies

Dynamic proxies in Java is a simple and very useful feature.

Usually we create an interface implementation and then compilation is involved. With dynamic proxies we can implement a list of interfaces at runtime. A proxy object will be created, when a method is invoked on that proxy instance, the methods invoked will be forwarded to an invocation handler specified.

This can have various usages. A common use case would be for a java interface which we can use a proxy and intercept the calls to the methods invoked.

Supposing we have a JDBC connection pool and we want to have something like a micrometer counter. On getting a connection the counter will increase, thus we can identify the rate of acquiring connections in our application.

We shall use a proxy for that.

Let’s first add a docker container for Postgresql using Docker Compose.

version: '3.1'

services:
  postgres:
    image: postgres
    restart: always
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - 5432:5432

We can run with the following command

docker compose up

You can find more on Compose on the Developers Essential Guide to Docker Compose.

Let’s add our dependencies to our Java project

    <dependencies>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>4.0.3</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

Now will also add a small snippet.

package com.example.gkatzioura.proxy;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class Application {

    public static void main(String[] args) throws SQLException {
        Properties props = new Properties();

        props.setProperty("dataSourceClassName", "org.postgresql.ds.PGSimpleDataSource");
        props.setProperty("dataSource.user", "postgres");
        props.setProperty("dataSource.password", "postgres");
        props.setProperty("dataSource.databaseName", "postgres");
        props.put("dataSource.logWriter", new PrintWriter(System.out));

        HikariConfig config = new HikariConfig(props);
        
        try(HikariDataSource ds = new HikariDataSource(config)) {
            try(Connection connection = ds.getConnection()) {
                System.out.println("Should be printed after the proxy");       
            }
        }
    }

}

We can examine the DataSource interface of Java on the source code. We can see the method of interest, getConnection, present.

public interface DataSource  extends CommonDataSource, Wrapper {

    Connection getConnection() throws SQLException;
    ...
}

Instead of creating an interface implementation and having to implement all those methods and then delegate them to the actual DataSource instance, we shall instead use a proxy and add actions only to the method of interest, in our case getConnection.

We shall implement an InvocationHandler.

package com.example.gkatzioura.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import javax.sql.DataSource;

public class GetConnectionHandler implements InvocationHandler {

    private final DataSource dataSource;

    public GetConnectionHandler(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("getConnection")) {
            System.out.println("Called before actual method");

        }

        return method.invoke(dataSource, args);
    }

    public static DataSource proxy(DataSource dataSource) {
        return (DataSource) Proxy.newProxyInstance(DataSource.class.getClassLoader(), new Class[]{DataSource.class}, new GetConnectionHandler(dataSource));
    }

}

Let’s break it down.

The invocation handler will be called for the method of an interface specified. When an interface method is invoked it is our choice how we shall handle it. In our case we shall print a simple message and then we shall execute the corresponding method to our target instance.

Also we have a static factory specified which shall proxy the object implementing the interface of interest. A new proxy instance will be created, it will implement the interfaces provided and the calls towards the proxy instance will be passed to the handler we provided.

Let’s revisit our main method.

package com.example.gkatzioura.proxy;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import static com.example.gkatzioura.proxy.GetConnectionHandler.proxy;

public class Application {

    public static void main(String[] args) throws SQLException {
        Properties props = new Properties();

        props.setProperty("dataSourceClassName", "org.postgresql.ds.PGSimpleDataSource");
        props.setProperty("dataSource.user", "postgres");
        props.setProperty("dataSource.password", "postgres");
        props.setProperty("dataSource.databaseName", "postgres");
        props.put("dataSource.logWriter", new PrintWriter(System.out));

        HikariConfig config = new HikariConfig(props);

        try(HikariDataSource ds = new HikariDataSource(config)) {
            DataSource dataSource = proxy(ds);
            try(Connection connection = dataSource.getConnection()) {
                System.out.println("Should be printed after the proxy");
            }
        }
    }

}

We wrapped the HikariDataSource with a Dynamic Proxy and if we run the program we should see the following output.

Called before actual method
Should be printed after the proxy

If we break it down, we created a proxy with the DataSource interface. By creating the proxy we provided an invocation handler which shall print a message before the getConnection method is invoked. The getConnection will be invoked by the actual implementation of the DataSource interface that we specified on the InvocationHandler.

You can find the source code on GitHub.