On Java threads and Linux

Yesterday, I asked a seemingly simple question: What is a Java thread?

In Linux, the concept of a thread is ambiguous. Schedulable units are created through clone, which defines familiar options such as sharing the same thread group (CLONE_THREAD) or sharing the same address spaces (CLONE_VM). The options span about a dozen items, defining I/O context sharing, file descriptors, signal handlers, and so on. The fork call defines few shared options, while pthread_create uses more shared context.

Does Java use pthread_create, fork, or its own variant of clone, or even its own userspace implementation? Java VMs obviously are free to differ in their implementations, but I had to sate my own curiosity regardless.

In order to test this, I created the most simple Java class using a thread:

public class HelloThread implements Runnable 
{
	public void run() 
	{
		System.out.println("Hello from a thread!");
	}

	public static void main(String args[]) 
	{
		System.out.println("About to create a thread");
		Thread thread = new Thread(new HelloThread());
		thread.start();

		try
		{
			thread.join();
		}
		catch (InterruptedException e)
		{
			System.err.println("Error in joining thread");
			return;
		}
		System.out.println("Thread finished");
	}
}


To trace its system calls, I executed the function strace -F -o HelloThread_java.out java -cp . HelloThread. The 2500+ line output may be downloaded here. The salient call:

22545 clone(child_stack=0x7fe0f77f6ff0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7fe0f77f79d0, tls=0x7fe0f77f7700, child_tidptr=0x7fe0f77f79d0) = 22558

It looks like a lot of shared context flags, which would point to pthread_create. To confirm the hypothesis, I also created some C code.

void * callback(void * ignored)
{
	printf("Hello from a thread!\n");
	return NULL;
}

int main()
{
	printf("About to create a thread\n");
	pthread_t thread;
	if(pthread_create(&thread, NULL, callback, NULL)) 
	{
		fprintf(stderr, "Error creating thread\n");
		return 1;
	}

	if(pthread_join(thread, NULL)) 
	{
		fprintf(stderr, "Error joining thread\n");
		return 1;
	}

	printf("Thread finished\n");
	return 0;
}

To trace the output, I ran strace -o HelloThread_c.out ./HelloThread. The 100-line output may be downloaded here. The salient clone call is:

22619 clone(child_stack=0x7fb2ef97aff0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7fb2ef97b9d0, tls=0x7fb2ef97b700, child_tidptr=0x7fb2ef97b9d0) = 22620

Yup, Java uses the same flags as pthread_create… on my box and on my code at least.

Why does this even matter? Database developers are accustomed to asymmetrical resource handling, and so need to bypass normal operating system functionality in favor of intelligent scheduling, such as writing their own I/O scheduling. The effect is magnified on distributed systems, where multiple computers synchronize on each other in addition to synchronizing on hardware.

In other words, a distributed system is the sum, and the bottleneck, of all its individual parts. Many developers avoid contending with this because they don’t have asymmetrical resource demands, or they are able to fit a well-defined solution for their needs such as Hadoop.

When I face a distributed system, I need to build up a mental model of what’s happening at the Linux level. On asking my question, I was told that my approach wasn’t Java-centric, because Java defines its own memory model, and is meant to abstract these things. Not knowing the details of this memory model, I reserved judgment. Still, I had to know.

This entry was posted in Linux and tagged , , . Bookmark the permalink. Follow any comments here with the RSS feed for this post. Post a comment or leave a trackback.

Leave a Reply

Your email address will not be published. Required fields are marked *

Your email address will never be published.