[Java Concurrency] Foundation

Posted by Terrum on Wed, 09 Oct 2019 00:56:05 +0200

I. overview

1.1 Thread and Process Differences

  • The process is an execution process of the program, the basic unit of the system running program, and the process is dynamic. System running a program is a process from creation, operation to extinction.
  • In Java, when we start the main function, we start a JVM process, and the thread of the main function is a thread in the process.
  • Every program running on the system is a process. Each process contains one or more threads. Threads are a set of instructions, or special segments of a program, which can be executed independently in the program. It can also be understood as the context in which the code runs. So threads are basically lightweight processes, which are responsible for performing multiple tasks in a single program. The operation system is usually responsible for the scheduling and execution of multiple threads.
  • Threads can be used to process tasks in long-occupied programs in the background. The running speed of programs may be accelerated. Threads are more useful in some waiting tasks, such as user input, file reading and writing, network sending and receiving data, etc. In this case, valuable resources such as memory usage can be released.
  • If there is a large number of threads, it will affect performance, because the operating system needs to switch between them, and more threads need more memory space. Usually block model data is shared among multiple threads, and thread deadlock needs to be prevented.
  • Summary: A process is a collection of all threads, and each thread is an execution path in the process.

Performance issues caused by 1.2 multithreading

  • It takes time to create and destroy threads. If a large number of threads are created and destroyed, the consumption of time will be more obvious, resulting in performance loss.
  • Extremely consuming CPU and memory: A large number of threads create, execute and destroy very consuming CPU and memory, which will directly affect the throughput of the system, resulting in a sharp decline in performance. If memory resources occupy more, it is likely to cause OOM.
  • It is easy to cause frequent execution of GC: the creation and destruction of a large number of threads can easily lead to frequent execution of GC, resulting in memory jitter, and memory jitter. For mobile terminals, the biggest impact is to create interface jamming.
  • In view of the above described problems, the solution is in the final analysis: reuse of existing threads, thereby reducing the creation of threads. So this involves the concept of an Executor Service. The basic function of a thread pool is to reuse threads. Knowledge about thread pools, to be sorted out later

2. Multithread Creation

2.1 First type - inheritance of Thread class

The code is as follows:

//1. Inheriting threads, rewriting run methods, and running methods require threads to execute code
class ThreadDemo01 extends Thread {
    // In the run method, threads are required to execute code
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.print("Sub thread id:" + this.getId() + ",");
            System.out.print("Sub thread name:" + getName()+",");
            System.out.println("Sub thread--->i:" + i);
        }
    }

}
// 1. what is thread thread is an execution path, each thread does not affect each other.
// 2. what is multithreading? Multithreading has many different execution paths in one process and execute in parallel. Aim To improve program efficiency.
// 3. In a process, there must be a main thread.
// 4. How to execute the program if there is no thread main thread.
// Several classifications of threads 1. User threads, daemon threads
//            2. Main Thread Subthread GC Thread
public class T001_CreateWithThread {
    // Alternate execution
    public static void main(String[] args) {
        System.out.println("main... Main thread starts...");
        // 1. Create threads
        ThreadDemo01 threadDemo01 = new ThreadDemo01();
        // 2. Start threads
        threadDemo01.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("main---> i:  " + i);
        }
        System.out.println("main... Main thread termination...");
    }
} 

Implementation results:

Ways of anonymous inner classes

System.out.println("-----Multithreading start-----");
new Thread() {
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("thread -- " + Thread.currentThread().getName() + "-->" + i);
            }
        };

    }.start();
System.out.println("-----Multithread Creation End-----");

2.2 Second Kind - Implementing Runnable Interface

The code is as follows:

class ThreadDemo02 implements Runnable {
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(" son i:" + i);
        }
    }
}

// 1. Implementing runable interface and rewriting run method
public class T002_CreateWithRunnable {
    public static void main(String[] args) {
        System.out.println("main... Main thread starts...");
    
        // Create thread
        ThreadDemo02 threadDemo02 = new ThreadDemo02();
        /*
        * Another construction method of Thread is used here.
        * This constructor can pass in a Runnable implementation class
        * And when we look at the source code of Thread, we can see that the Thread class implements Runnable originally.
        * A Thread object can also be passed in, so that run() in a Thread object can be transferred.
        * Method is invoked by other threads
        */
        Thread t1= new Thread(threadDemo02);
        // Startup thread
        t1.start();
        for (int i = 0; i <10; i++) {
            System.out.println("main..i:"+i);
        }
        System.out.println("main... Main thread termination...");
        
    }
}

Ways of anonymous inner classes

System.out.println("-----Multithread Creation Start-----");
Thread thread = new Thread(new Runnable() {
public void run() {
    for (int i = 0; i< 10; i++) {
        System.out.println("i:" + i);
    }
}
}).start();
System.out.println("-----Multithread Creation End-----");

2.3 Third Kind - Implementing Callable Interface

/**
 * 
 * Callbale Interfaces can return values and throw exceptions.
 * The run method in the Runnable interface does not return a value, and exceptions can only be caught.
 * 
 * @author hao
 *
 */
public class T002_CreateWithCallable {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        MyCallable mc = new MyCallable();
        FutureTask<Integer> ft = new FutureTask<Integer>(mc);
        Thread thread = new Thread(ft);
        thread.start();
        System.out.println(ft.get());
    }
}

class MyCallable implements Callable<Integer> {

    public Integer call() throws Exception {
        
        return 124;
    }
    
}

2.4 Common Thread Constructors

  • Thread(), assigning a new Thread object
  • Thread(String name), which assigns a new Thread object with the specified name
  • Thread(Runable target), assigning a new Thread object to target as its running object
  • Thread(Runable target, String name) assigns a new Thread object, takes target as its running object, and specifies name

2.5 Is it better to inherit the Thread class or to implement the Runnable interface?

  • The implementation of Runnable interface is good, because the interface can continue to inherit, inherited classes can not inherit.
  • Is the start thread using the call start method or the run method?
  • Start executing threads. Note that starting threads is not a call to run methods, but a start method.
  • run is a method called using an instance.

3. Thread Basic Knowledge

3.1 Overview of commonly used thread API s

  • Start () - - - > Start Threads
  • Thread. current Thread () - > Gets the current thread object
  • GetID () - > Gets the current thread ID
  • GetName () - > Gets the current thread name in the default format of Thread - number (which starts at 0)
  • SetDaemon (true) - > Set to daemon thread
  • IsAlive () - > to determine whether the current thread is active or not. The thread is running or ready to start running. It is considered that the thread is alive.
  • Thread. sleep (long mill) - > sleeping threads
  • Thread. yield () - > is to abandon the current CPU resources and give it to other tasks to occupy CPU execution time, but the abandonment time is uncertain. It may just give up and get CPU time slice immediately.
  • Join () - - - > Delivers execution to the thread, and the current thread is blocked.
  • SetPriority ((int new Priority) - > Sets the priority of threads, with priority of 1 - 10, default of 5, 10 being the highest level.
  • Stop () - > is out of date, use cautiously and stop threads, which may result in lock release after stop, and some cleaning may not be completed.
  • suspend() and resume () - > are out of date, pausing and restoring threads can cause the exclusive problem of threads, and may cause data asynchronization.

3.2 daemon thread

  • There are two kinds of threads in Java, one is user thread, the other is daemon thread.
  • User threads refer to user-defined created threads. The main thread stops and the user thread does not stop.
  • Daemon threads are also stopped when the process does not exist or the main thread stops. A typical daemon thread is the garbage collection thread. When there are no non-daemon threads in the process, there is no need for the garbage collection threads to exist and they are automatically destroyed.
  • We can use the setDaemon(true) method to set the daemon thread

Example

/*
* What is daemon thread? Daemon thread thread is also destroyed automatically.
* In this example, we manually set the child threads to daemon threads.
* Daemon threads also terminate when other threads (in this case only the main thread) stop
*/
public class DaemonThread {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                    System.out.println("I am a sub-thread...");
                }
            }
        });
        thread.setDaemon(true);
        thread.start();
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(100);
            } catch (Exception e) {
    
            }
            System.out.println("I am the main thread");
        }
        System.out.println("The main thread has finished executing!");
    }
    
}

Operation result

3.3 yield method

  • The role of the Thread.yield() method: pause the currently executing thread and execute other threads. (It may not work)
  • yield() returns the currently running thread to a runnable state to allow other threads with the same priority to run. Therefore, the purpose of using yield() is to enable proper rotation between threads with the same priority. However, yield() is not guaranteed to achieve the purpose of concession in practice, because the concessional thread may be selected again by the thread scheduler.
  • CONCLUSION: In most cases, yield() will cause a thread to move from a running state to a runnable state, but it may not work.

Sample code

/**
 * yield The purpose of the method is to abandon the current CPU resources.
 * Give it to other tasks to take up CPU execution time.
 * But the time to give up is uncertain. It is possible to get CPU time slice just after giving up.
 * @author hao
 *
 */
public class T009_Yield {
    public static void main(String[] args) {
        YeildTestThread t1 = new YeildTestThread();
        t1.start();
    }
}

class YeildTestThread extends Thread{
    @Override
    public void run() {
        super.run();
        
        long beginTime = System.currentTimeMillis();
        int count =0;
        for(int i =0;i<5000000;i++) {
            //Thread.yield();
            count = count +(i+1);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("When used:"+(endTime-beginTime)+"Millisecond!");
        
    }
}

results of enforcement
Comment out Thread.yield(); it's different from the execution time without commenting out.

3.4 join() method function

  • When the t1.join() method is executed in the main thread, it is considered that the main thread should give the execution right to t1.

Code example

//Create a thread, and the main thread can execute only after the sub-thread has finished executing.
public class T010_Join {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {

            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("Sub thread,i:" + i);
                }
            }
        });
        t1.start();
        // The current thread releases the eligibility right and waits until t1 has been executed before continuing to execute.
        t1.join();
        for (int i = 0; i < 5; i++) {
            System.out.println("main thread,i:" + i);
        }
    }

}

results of enforcement

Application scenario

There are three threads: T1, T2 and T3. How do you ensure that T2 is executed after T1 is executed? T3 is executed after T2 is executed as follows:

public class JoinThreadDemo02 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println("t1,i:" + i);
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                try {
                    t1.join();
                } catch (Exception e) {
                }
                for (int i = 0; i < 20; i++) {
                    System.out.println("t2,i:" + i);
                }
            }
        });
        Thread t3 = new Thread(new Runnable() {
            public void run() {
                try {
                    t2.join();
                } catch (Exception e) {
                }
                for (int i = 0; i < 20; i++) {
                    System.out.println("t3,i:" + i);
                }
            }
        });
        t1.start();
        t2.start();
        t3.start();
    }
}

3.5 Priority

  • The modern operating system basically uses time division to schedule the threads running. The number of time slices allocated by the thread determines the number of processor resources used by the thread, and also corresponds to the concept of thread priority. In JAVA threads, priority is controlled by an int priority, ranging from 1 to 10, of which 10 is the highest and the default value is 5.
public class T011_Priority {
    public static void main(String[] args) {
        PrioritytThread prioritytThread = new PrioritytThread();
        Thread t1 = new Thread(prioritytThread);
        Thread t2 = new Thread(prioritytThread);
        t1.start();
        // Note that setting priorities does not mean that they will be executed every time. Only CPU scheduling has limited allocation
        t1.setPriority(10);
        t2.start();

    }
}
class PrioritytThread implements Runnable {

    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().toString() + "---i:" + i);
        }
    }
}

4. Stop Threads

  • There are three ways to terminate running threads in Java:
  • Using the exit flag is the normal exit of the thread, that is, when the run method is completed, the thread terminates.
  • The stop method is not recommended for forcibly terminating threads. It is outdated and will release the lock after calling the stop method, which may not complete some cleanup work.
  • Interrupt threads using interrupt method

Introduction of 4.1 interrupt method

After calling the interrupt() method, the thread does not stop immediately. It just marks a stop in the current thread, not the real stop thread.

4.2 Determine whether the thread is stopped

  • Thread.interrupted(): Test whether the current thread has been interrupted, and determine the interrupt status is cleared by this method. If this method is called twice in a row, the second time it returns false
  • this.isInterrupted(): Tests whether the thread Thread object is already in an interrupt state, but does not clear the status tag

Source code is as follows

  • You can see that both of the above methods call the local method isInterrupted(), passing in true and false to determine whether to clear the interrupt state or not.
public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

public boolean isInterrupted() {
    return isInterrupted(false);
}

/**
 * Tests if some Thread has been interrupted.  
 * The interrupted state  is reset or not 
 * based on the value of ClearInterrupted that is passed.
 * 
 * Determine whether certain threads have been interrupted.
 * Decide whether to reset the status of the interrupt based on the incoming ClearInterrupted value
 */
private native boolean isInterrupted(boolean ClearInterrupted);

4.3 Stop threads by exception

/**
 * Termination of threads by throwing exceptions
 * @author hao
 *
 */
public class Test_ExceptionInterrupt {
    public static void main(String[] args) {
        try {
            ExcepThread t = new ExcepThread();
            t.start();
            Thread.sleep(2000);
            t.interrupt();
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }
}

class ExcepThread extends Thread{
    @Override
    public void run() {
        this.stop();
        try {
            for(int i=0;i<500000;i++) {
                if(Thread.interrupted()) {
                    System.out.println("It's stopped. I'm going to quit.!");
//              break;
                    throw new InterruptedException();
                }
                System.out.println("i "+(i+1));
            }
            System.out.println("I was exported. Thread does not stop! just for The loop was interrupted");
        } catch (InterruptedException e) {
            System.out.println("catch t ");
            e.printStackTrace();
        }
    }
}

V. Multithread Running State

5.1 Thread Status Overview

  • Threads are always in one of the following five states: NEW, Runnable, Blocked, Waiting, Timed Waiting, Terminated, as follows:

5.2 New State (NEW)

  • When a thread is created with the new operator, such as new Thread(r), the thread is not running yet, and the thread is in a new state. When a thread is in a new state, the program has not yet started running the code in the thread.

5.3 Runnbale

  • It may be running or waiting for CPU time slices. Contains Ready and Running status in the operating system thread state.
  • Ready
    • A newly created thread does not start automatically. To execute a thread, you must call the thread's start() method. When the thread object calls the start() method, the thread is started. The start() method creates system resources for the thread to run and dispatches the thread to run() method. When the start() method returns, the thread is ready.
    • Threads in a ready state do not necessarily run the run() method immediately. Threads must also compete with other threads for CPU time. Only when CPU time is obtained can they run threads. Because in a single CPU computer system, it is impossible to run multiple threads at the same time, only one thread is running at a time. So there may be multiple threads ready at this point. Threads in a ready state are scheduled by the thread scheduler of the Java runtime system.
  • Running
    • When a thread obtains CPU time, it enters the running state and actually starts executing the run() method.

5.4 Blocked State

  • Threads try to get a lock that is being held by other threads; if their thread releases the lock, it ends the state.

5.5 Waiting

  • Wait for other threads to wake up explicitly, otherwise CPU time slices will not be allocated.

5.6 Timed Waiting

  • There is no need to wait for other threads to wake up explicitly, and the system will wake up automatically after a certain time.

5.7 Terminated

  • There are two reasons for thread death:
  • run method withdraws normally and dies spontaneously.
  • An uncovered exception terminates the run method and causes the thread to die suddenly.

  • In order to determine whether the thread is currently alive (either runnable or blocked), the isAlive method is needed. If it is runnable or blocked, this method returns true; if the thread is still new and not runnable, or if the thread dies, it returns false.

Topics: Java jvm network Mobile