JavaRush /Java Blog /Random EN /Threads Don't Spoil Java: Part I - Threads
Viacheslav
Level 3

Threads Don't Spoil Java: Part I - Threads

Published in the Random EN group

Introduction

Multithreading has been built into Java since the very early days. So let's take a quick look at what multithreading is about. You can't screw up Java with Threads: Part I - Threads - 1Let's take as a starting point the official lesson from Oracle: " Lesson: The "Hello World!" Application ". Let's change the code of our Hello World application a little to the following:
class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsis an array of input parameters passed when starting the program. Let's save this code to a file with a name that matches the class name and extension .java. Let's compile with the javac utility : javac HelloWorldApp.java After that, call our code with some parameter, for example, Roger: java HelloWorldApp Roger You can't screw up Java with Threads: Part I - Threads - 2Our code now has a serious flaw. If we do not pass any argument (i.e. just execute java HelloWorldApp), we will get an error:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
An exception (i.e. an error) occurred in thread (in a thread) with the name main. It turns out that there are some threads in Java? From here our journey begins.

Java and Threads

To understand what a thread is, you need to understand how a Java application starts up. Let's change our code like this:
class HelloWorldApp {
    public static void main(String[] args) {
		while (true) {
			//Do nothing
		}
	}
}
Now let's compile this again with javac. Next, for convenience, let's run our Java code in a separate window. On Windows, this can be done like this: start java HelloWorldApp. Now, using the jps utility , let's see what information Java will tell us: You Can't Mess Java with Threads: Part I - Threads - 3The first number is the PID or Process ID, the process ID. What is a process?
Процесс — это совокупность codeа и данных, разделяющих общее виртуальное addressное пространство.
With the help of processes, the execution of different programs is isolated from each other: each application uses its own memory area without interfering with other programs. For more details, I advise you to read the article: " https://habr.com/post/164487/ ". A process cannot exist without threads, so if a process exists, it has at least one thread. How does this happen in Java? When we run a Java program, its execution begins with the main. We kind of enter the program, so this special method mainis called the entry point, or "entry point". The method mainmust always be public static voidso that the Java Virtual Machine (JVM) can start executing our program. For details, see Why is the Java main method static?". It turns out that java launcher (java.exe or javaw.exe) is a simple application (simple C application): it loads various DLLs, which are actually JVMs. Java launcher executes a specific set of Java Native Interface (JNI) calls "JNI is a mechanism that connects the world of the Java virtual machine and the world of C ++. It turns out that the launcher is not a JVM, but its loader. He knows what the right commands need to be executed to start the JVM. Knows how to organize all the necessary environment using JNI calls This organization of the environment also includes the creation of the main thread, which is usually called main... To better see what threads live in a java process, we use the jvisualvm program, which is included with the JDK. Knowing the pid of the process, we can open the data immediately on it: jvisualvm --openpid айдипроцесса You Can't Mess Java With Threads: Part I - Threads - 4Interestingly, each thread has its own separate area in the memory allocated for the process. This memory structure is called a stack. The stack is made up of frames. A frame is a method call point, an execution point. A frame can also be represented as a StackTraceElement (see Java API for StackTraceElement ). You can read more about the memory allocated to each thread here . If we look at the Java API and search for the word Thread there, we will see that there is a java.lang.Thread class . It is this class that represents a thread in Java, and we have to work with it. You Can't Mess Java With Threads: Part I - Threads - 5

java.lang.Thread

A thread in Java is represented as an instance of the java.lang.Thread. It should be immediately understood that instances of the Thread class in Java are not threads themselves. This is just a kind of API for low-level threads that are managed by the JVM and the operating system. When using the java launcher we launch the JVM, it creates a main thread with a name mainand a few more service threads. As the JavaDoc of the Thread class says: When a Java Virtual Machine starts up, there is usually a single non-daemon thread There are 2 types of threads: daemons and non-daemons. Daemon threads are background (service) threads that perform some work in the background. Such an interesting term is a reference to "Maxwell's demon", which you can read more about in Wikipedia in the article about " demons ". As stated in the documentation, the JVM continues the execution of the program (process) until:
  • Runtime.exit method not called
  • All NOT daemon threads completed their work (both without errors and throwing exceptions)
Hence the important detail: daemon threads can be terminated on any executable command. Therefore, the integrity of the data in them is not guaranteed. Therefore, daemon threads are suitable for some service tasks. For example, Java has a thread that is responsible for processing finalize methods or threads that are related to the garbage collector (Garbage Collector, GC). Each thread is included in some group ( ThreadGroup ). And groups can enter into each other, forming some kind of hierarchy or structure.
public static void main(String []args){
	Thread currentThread = Thread.currentThread();
	ThreadGroup threadGroup = currentThread.getThreadGroup();
	System.out.println("Thread: " + currentThread.getName());
	System.out.println("Thread Group: " + threadGroup.getName());
	System.out.println("Parent Group: " + threadGroup.getParent().getName());
}
Groups allow you to streamline the management of flows and keep track of them. In addition to groups, threads have their own exception handler. Let's look at an example:
public static void main(String []args) {
	Thread th = Thread.currentThread();
	th.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
		@Override
		public void uncaughtException(Thread t, Throwable e) {
			System.out.println("An error occurred: " + e.getMessage());
		}
	});
    System.out.println(2/0);
}
Division by zero will cause an error, which will be caught by the handler. If you do not specify the handler yourself, the default handler implementation will work, which will display the error stack in StdError. You can read more in the review http://pro-java.ru/java-dlya-opytnyx/obrabotchik-neperexvachennyx-isklyuchenij-java/ ". In addition, a thread has a priority. You can read more about priorities in the article " Java Thread Priority in Multithreading ". You Can't Mess Java With Threads: Part I - Threads - 6

Create a thread

As stated in the documentation, we have 2 ways to create a thread. The first is to create your heir. For example:
public class HelloWorld{
    public static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Hello, World!");
        }
    }

    public static void main(String []args){
        Thread thread = new MyThread();
        thread.start();
    }
}
As you can see, the task is launched in the method run, and the thread is launched in the start. Do not confuse them, because. if we run the method rundirectly, no new thread will be started. It is the method startthat asks the JVM to create a new thread. The option with a descendant from Thread is bad already because we include Thread in the class hierarchy. The second minus is that we are starting to violate the SOLID "Single Responsibility" principle, because our class becomes both responsible for managing the thread and for some task to be performed on that thread. How is it right? The answer is in the very method runthat we are overriding:
public void run() {
	if (target != null) {
		target.run();
	}
}
Here targetis some java.lang.Runnablethat we can pass to Thread when creating an instance of the class. Therefore, we can do this:
public class HelloWorld{
    public static void main(String []args){
        Runnable task = new Runnable() {
            public void run() {
                System.out.println("Hello, World!");
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
It has also Runnablebeen a functional interface since Java 1.8. This allows you to write task code for threads even more beautifully:
public static void main(String []args){
	Runnable task = () -> {
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

Total

So, I hope, from this story it is clear what a stream is, how they exist and what basic operations can be performed with them. The next part is to understand how threads interact with each other and what their life cycle is. #Viacheslav
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION