I had a hard time doing Java multithreading from scratch, so organize it

Trigger

It was when JustSystems did a "100 Java knock" to improve the technology. https://github.com/JustSystems/java-100practices/blob/master

I was stuck with a problem related to multithreading, and it took me two hours to complete one question. Moreover, even if I googled variously and cleared it, there were various parts that I did not understand well, so I will organize it as a memorandum.

I did it in class a long time ago, but I didn't even know how to use it, so I couldn't remember it ...

This article addresses the issue of 100-knock multithreading above.

Notice

I'm studying so there may be mistakes I try not to write wrong things as much as possible, but this is not the case! I would be very happy if you could comment.

In addition, there is a description of the premise that you have seen the Java 100 knock mentioned above. Please note.

Multithreading basics in Java

The explanation was easy to understand here. https://eng-entrance.com/java-thread

Inherit the Thread class, or in a class that implements the Runnable interface, use the run () method. The basic form is to override and describe the process you want to perform in multiple threads. (Thread can be created without defining a class ... See below)

Let's make a sample. Create two threads that output different character strings to standard output and run them in parallel.

ThreadSample.java


/**
 *Basic thread
 *
 */
public class ThreadSample extends Thread{
	// run()Override and write the process in it
	@Override
	public void run() {
		for(int i = 0; i < 300; i++) {
			System.out.println("Multi-"+i);
		}
	}
}

ThreadSampleMain.java


/**
 *Basic thread execution class
 *Execute in parallel with ThreadSample
 *
 */
public class ThreadSampleMain {

	public static void main(String[] args) {
		//New a class that inherits Thread
		ThreadSample thread = new ThreadSample();
		// .start()When you run the method, run()The process written in works
		thread.start();
		for(int i=0; i < 300; i++) {
			System.out.println("Main-"+i);
		}
	}

}

When I try to move it, it looks like this.

(Abbreviation)
Main-297
Multi-0 #The contents of the other thread are out even though Main is not finished here
Multi-1
Multi-2
Multi-3
Multi-4
Multi-5
Multi-6
Multi-7
Multi-8
Multi-9
Multi-10
Multi-11
Multi-12
Multi-13
(Abbreviation)

However, this simplest thread is not guaranteed where the ThreadSample thread will be executed each time it is executed. There are various mechanisms that can guarantee this.

You can use threads without declaring a name

I created an instance of ThreadSample with the name thread in the code above, but you can use it without declaring the name. When I rewrite Main, it looks like this.

ThreadSampleMain2


public class ThreadSampleMain2 {

	public static void main(String[] args) {
		//New a class that inherits Thread and start it as it is
		new ThreadSample().start();
		for(int i=0; i < 300; i++) {
			System.out.println("Main-"+i);
		}
	}
}

Looking at the answer example of 100 knocks, there are many such notations. I'm ashamed to say that I didn't know that I could write the method execution process as it is in the new object that I was doing in Java.

You can use it without making it a class in the first place

Patterns seen in Java 100 knock Q041 answer examples, etc.

ThreadSampleMain3


public class ThreadSampleMain3 {

	public static void main(String[] args) {
		//Execute the new anonymous thread as it is
		new Thread() {
			@Override
			public void run() {
				for(int i = 0; i < 300; i++) {
					System.out.println("Multi-"+i);
				}
			}
		}.start();

		for(int i=0; i < 300; i++) {
			System.out.println("Main-"+i);
		}
	}

}

I haven't seen it actually used in enterprise systems so far, but it seems that you can write it like this.

Put Runnable in Thread instance?

Looking at the answer example of Java100 knock, there are a lot of patterns that start () is assigned to the instance of Thread class instead of directly start () the class that implements Runnable. ..

For example, the following is quoted from the answer example of Q040.

public class Answer040 implements Runnable {
    
    /**
     *040's answer.
     *Stack trace of uncaught exception
     *Output standard error with current time.
     * 
     * @param arguments not used.
     */
    public static void main(final String[] args) {
        Thread thread = new Thread(new Answer040());
        
        //Register the handler that implements UncaughtExceptionHandler with the setUncaughtException method.
        thread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        
        //Execution of main thread.
        thread.start();
    }
    
    /**
     *Run thread.
     */
    public void run() {
        //sleep.
        try {
             Thread.sleep(500L);
        } catch (InterruptedException e) {
             e.printStackTrace();
        }
        
        Thread subThread = new Thread(new SubThread());
        
        //Associate a handler that implements UncaughtExceptionHandler with a subthread.
        subThread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        
        //Execution of subthread.
        subThread.start();
    }
}

After reading this example answer, I had a question.

--Why are you overriding run ()? Is it not good to directly new subThread () and start () it? --What is the sleep before run ()?

For example, should we be able to do the same by rewriting it like this?

public class Answer040-2 implements Runnable {

    public static void main(final String[] args) {
        //note:SubThread is a class that implements Runnable
        SubThread sub = new SubThread();
        sb.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        sub.start();
    }

In conclusion, ** I can't. ** (Compile error)

Reasons to register a Runnable implementation instance in Thread

Here in the code above

public class Answer040 implements Runnable {

    /**
     *040's answer.
     *Stack trace of uncaught exception
     *Output standard error with current time.
     * 
     * @param arguments not used.
     */
    public static void main(final String[] args) {
        Thread thread = new Thread(new Answer040()); //← here
(Abbreviation)

In the first place, let's take a look at Javadoc to see what it is like to put an instance that inherits Runnable in the argument of Thread instance.

public Thread(Runnable target) Assign a new Thread object. This constructor has the same effect as Thread (null, target, gname) (gname is the newly generated name). The automatically created name takes the form "Thread-" + n, where n is an integer. Parameters: target --An object that contains the run method that will be called when this thread starts. If null, the run method of this class does nothing.

I see. If there is a constructor that works the same. I'll take a look there as well.

public Thread(ThreadGroup group, Runnable target, String name) Assigns a new Thread object that belongs to the thread group referenced by group, with target as its execution object and the name specified as its name. If a security manager exists, the checkAccess method is called with ThreadGroup as its argument. In addition, if called directly or indirectly from the constructor of a subclass that overrides the getContextClassLoader or setContextClassLoader method, the checkPermission method will be called with RuntimePermission ("enableContextClassLoaderOverride") permissions.

In other words, the Thread constructor has a constructor that can omit the thread group (ThreadGroup) and thread name (String), and the constructor that has Runnable as an argument, which was mentioned in the previous code, automatically assigns the thread group and thread name. ..

So what this constructor does is

Assign a new Thread object.

It seems that. Let's find out what it means to assign a Thread object. https://www.task-notes.com/entry/20151031/1446260400

According to the above site, Thread class is implemented like ** to operate run () of Runnable object passed as an argument **. In other words, passing your own thread to the argument Runnable means executing this with run (). We also found that implements Runnable is better than ** extends Thread (unless you override anything other than run () **. Perhaps you should refrain from extends because you can't inherit multiple times.

From these, the conclusion obtained at this point is that ** it is passed to Thread without direct new because it uses the run () method of the java.lang.Thread superclass **. I presume that it is convenient for the thread group and ExceptionHandler method described later.

Why sleep before subthreading

Here in the previous answer example

(Omitted)
/**
     *Run thread.
     */
    public void run() {
        //sleep.
        try {
             Thread.sleep(500L); //← here
        } catch (InterruptedException e) {
             e.printStackTrace();
        }
        
        Thread subThread = new Thread(new SubThread());
        
        //Associate a handler that implements UncaughtExceptionHandler with a subthread.
        subThread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        
        //Execution of subthread.
        subThread.start();
    }

This process often came up, and my senior at work also said, "I don't know why, but if I don't put this in, it won't work." I tried various googles, but it didn't come out.

Is it because setUncaughtExceptionHandler () may not succeed unless you wait for a moment after Thread written directly in the main method is started ()? I'm guessing. For the time being, remember that it's a magic for now.

Digression: About setUncaughtExceptionHandler ()

This is the method that needs to be used in Java 100 knock Q040. If you create a class that implements an interface called UncaughtExceptionHandler and override uncaughtException (Thread, Trowable) in it, when a thread throws an exception that could not be caught, this method will be executed just before the thread terminates abnormally. Called. ** Useful for example log output of exceptions ** Reference: https://javazuki.com/articles/uncaught-exception-handler-usage.html

And this is a method of Java.lang.Thread. ** Runnable doesn't have it, so it seems better to use it by putting an instance of Runnable implementation in Thread **. The reason why I couldn't suddenly new the subThread class in the previous chapter was that I was trying to call a method that wasn't there, so I got a compile error.

Register to a thread group

Thread group: A mechanism that allows you to group threads to monitor the number of active threads and suspend multiple threads. If you use a thread group, you can set a name for the thread at the time of registration, and you can manage it within the scope of the name in the thread group, so it seems to be nice without mixing with ordinary variables.

Notation that appears in the answer to 100 knock questions 35 https://github.com/JustSystems/java-100practices/blob/master/contents/035/README.md

Below, ** quoted from the answer to Q35 **

public final class Answer035 implements Runnable {
    /*Thread group A. */
    private static ThreadGroup groupA = new ThreadGroup("GroupA");
    
    /*Thread group B. */
    private static ThreadGroup groupB = new ThreadGroup("GroupB");
    
    /**
     *Group A,Execute 100 threads each of B threads.
     */
    @Override public void run() {
        for (int i = 0; i < 100; i++) {
            new Thread(groupA, new ThreadRun(), "thread" + i).start();
        }
        
        for (int i = 0; i < 100; i++) {
            new Thread(groupB, new ThreadRun(), "thread" + i).start();
        }
    }
    
    /**
     *Output the number of active threads in each thread group.
     *
     * @param point Count count
     */
    public static void printActiveCount(int point) {
        System.out.println("Active Threads in Thread Group " + groupA.getName() +
            " at point(" + point + "):" + " " + groupA.activeCount());
    
        System.out.println("Active Threads in Thread Group " + groupB.getName() +
            " at point(" + point + "):" + " " + groupB.activeCount());
    }
    
    /**
     *035's answer.
     *Execute threads for each thread group,
     *Standard output of the number of active threads in each thread.
     *
     * @param arguments not used.
     */    
    public static void main(String[] args) throws InterruptedException {
        /*Allocate a new thread. */
        Thread thread = new Thread(new Answer035());
        
        /*Run the thread. */
        thread.start();
        
        //Output the number of active threads.
        for (int i = 1 ;; i++) {
            printActiveCount(i);
            thread.sleep(1000L);
            
            //Exit the loop when the active thread reaches 0.
            if (groupA.activeCount() == 0 && groupB.activeCount() == 0) {
                break;
            }
        }
    }
}

Override part of run () of this. It is trying to register its own Thread inheritance class ThreadRun in the thread group. When managing in a Thread group, you can use the constructor of the Thread class that can take Thread as the second argument and create a new one there without declaring the names of multiple threads one by one.

Exclusive processing between threads

In multiple threads, this process is used when it is a problem if it is done before this process. There are various methods, and it becomes complicated at once from this area, so I will summarize by method

Terminology

Thread safe

It means that some processes and some processes do not cause any problems even if they are executed in parallel. It feels like a "thread-safe method".

It's fine if you just output variables that are completely unrelated as before, but it is thread-safe to have a method that refers to a static variable and a method that rewrites it, and to refer to it before rewriting and throw a NullPointerException. Absent.

There are various processing methods to make it thread-safe. While running one, stop the other. Reference source: https://wa3.i-3-i.info/word12456.html

Thread pool

It is faster to reuse the thread than to create a new thread, so it is a method to reuse the created thread. The thread pool is created by Executor (Executor Service).

synchronized Pattern to use

Processing written by adding "synchronized" to the method declaration of java. Methods with this will not be executed at the same time, and execution can be controlled by methods such as notify () and wait (). I'm talking about myself, but even when I was a student studying sloppyly, I remembered only these two things, so I have a bitter memory of saying something stupid, such as "multithreading or synchronizing."

This appears even with 100 Java knocks, but I got stuck when I tried it, so make a note.

Patterns you shouldn't do

From Java 100 knock Q041

Using wait () and notify (), "add integers from 1 to 10000 and store the result in a global variable" Thread A and "After the operation of Thread A ends, the value of the global variable is set to standard output. Implement a "output" thread B and a program that starts threads A and B at about the same time.

スレッドA:Q41_ThreadA.java スレッドB:Q41_ThreadB.java 処理2つとグローバル変数のクラス:Q41_number.java 実行用:Q41.java It was implemented as follows.

Q41_number


public class Q41_number {

	public static long number = 0;

	public synchronized void addNumber() {
		System.out.println("add number...");
		for(long i = 1; i <= 10000; i++) {
			number += i;
		}
		System.out.println("end");
		//Notify that it is finished
		notify();
	}

	public synchronized void showNumber() {
		try {
			System.out.println("waiting...");
			wait();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(number);
	}
}

Q41_ThreadA


public class Q41_ThreadA implements Runnable{
	Q41_number q41 = new Q41_number();

	@Override
	public void run() {
		// wait()Notify before()Then it loops infinitely, so wait for a while
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
		q41.addNumber();
	}
}

Q41_ThreadB


public class Q41_ThreadB implements Runnable{
	Q41_number q41 = new Q41_number();

	@Override
	public void run() {
		//Make it move slightly ahead of Thread A
        try {
            Thread.sleep(500L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
		q41.showNumber();
	}
}

Q41


public class Q41 {

	public static void main(String[] args) {
		Thread threadA = new Thread(new Q41_ThreadA());
		Thread threadB = new Thread(new Q41_ThreadB());
		//Execute
		threadA.start();
		threadB.start();
	}
}

When executed ** Q41_number # showNumber () stops at wait () and stops working **

Reason

http://www.ne.jp/asahi/hishidama/home/tech/java/thread.html I wrote in the comments in the synchronized chapter of this site.

// ↑ Even if func1 () and func2 () of the same instance are called from different threads at the same time, only one of them will be executed. // The same applies to calling func1 () and func1 () of the same instance from different threads. // If it is a different instance, it will not be exclusive because the lock object is different, and it will be executed at the same time.

In the previous code, the Q41_number instances of ThreadA and ThreadB are different. ** Instances with synchronized methods should be shared by multiple threads. ** **

About volatile

Can be set when defining a field. With this, the field will not be cached during compiler optimization. It's fluffy, but I suspect that the cache is being used when I say, "This thread for each field should have been rewritten, but when I debug it, it hasn't been rewritten ...".

The following sites are detailed about the cache and the phenomena caused by it. https://www.sejuku.net/blog/65848

Q017 uses this when it is a Java 100 knock.

Execution order definition and end wait for each thread

This is useful when you have multiple threads and you want to order them by thread. Use java.util.concurrent.CountDownLatch. This uses await (), which waits until the value set when it is defined becomes zero, and countDownLatch (), which reduces the number by 1, to realize waiting until all threads are finished.

Recommended Posts

I had a hard time doing Java multithreading from scratch, so organize it
Java had a QR code creation library called QRGen that wrapped ZXing nicely, so I tried it.
I had a hard time installing MariaDB 10.5.5 on CentOS 8 of Sakura VPS
Android: I had a hard time displaying the HTML file on the SD card
The story that docker had a hard time
I tried hitting a Java method from ABCL
left4dead2 I made a Docker image for the server and tried running it on GCE # 3 (I had a hard time building the server)
[Solution] A memo that I had a hard time because the format of sinatra-validation changed
A story that I had a hard time trying to build PHP 7.4 on GCE's CentOS 8
[Note] Create a java environment from scratch with docker
I made a Wrapper that calls KNP from Java
Java SE 13 (JSR388) has been released so I tried it