Threads for java Concurrent Programming

Several ways to create threads

1. Inherit Thread class and exist as Thread object (inherit Thread object)

	public class CreateThreadTest01 extends Thread{
    public CreateThreadTest01(String CreateName){
        super(CreateName);
    }
    @Override
    public void run(){
        while (!interrupted()){
            System.out.println(getName()+"Thread executed......");
            try{
                Thread.sleep(2000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        CreateThreadTest01 test01 = new CreateThreadTest01("Name1");
        CreateThreadTest01 test02 = new CreateThreadTest01("Name2");
        test01.start();
        test02.start();
        //test01.interrupt();// Interrupt thread 1
    }
}

Interrupted method, which can be used to determine whether the thread is interrupted. (the stop method is not allowed for terminating threads, which will not release the occupied resources. Therefore, when designing programs, we should design according to the idea of interrupting threads, just like example code).

2. Implement the runnable interface and exist as a thread task

public class CreateThreadTest02 implements Runnable{
    @Override
    public void run() {
        System.out.println("The thread executed successfully");
    }

    public static void main(String[] args) {
        // Passing a thread task to a thread object
        Thread thread = new Thread(new CreateThreadTest02());
        //Start thread
        thread.start();
    }
}

Runnable is only used to decorate the task executed by the thread. It is not a thread object. To start the runnable object, you must put it in a thread object.

3. Creating thread objects using anonymous inner classes

public class CreateThreadTest03 extends Thread{
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run(){
                System.out.println("Parameterless thread created successfully!");
            }
        }.start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("The thread object with the thread task executes");
            }
        }).start();
        // Create a thread object with a threaded task and an overridden run method
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("runnable run The thread executes....");
            }
        }){
            @Override
            public void run(){
                System.out.println("Override run Yes");
            }
        }.start();
        // The overridden method called should be the run method of the Thread class. Instead of the run method of the Runnable interface.
    }
}

4. Create a thread with a return value using the interface Callable

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class CreateThreadTeat04 implements Callable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CreateThreadTeat04 teat04 = new CreateThreadTeat04();
        // The final implementation is the Runnable interface
        FutureTask<String> task = new FutureTask<String>(teat04);
        Thread thread = new Thread(task);
        thread.start();
        System.out.println("Ha ha ha ha ha ha ha");
        String str = task.get();
        System.out.println("The result in the thread is:"+str);
    }
    @Override
    public Object call() throws Exception {
        Thread.sleep(2000);
        return "ABCD";
    }
}

The code of the Callable interface is as follows:

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

5. Timer timer

import java.util.Timer;
import java.util.TimerTask;

public class CreateThreadTest05 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("The thread of the timer executed......");
            }
        },0,1000);
    }
}

6. Thread pool create thread
As the name suggests, a thread pool is to create several executable threads in advance and put them into a pool (container). When necessary, the threads obtained from the pool do not need to be created by themselves. After use, the threads do not need to be destroyed, but put them back into the pool, so as to reduce the cost of creating and destroying Thread objects.
The Executor interface in Java version 5 or later defines a tool for executing threads. Its subtype, the thread pool interface, is ExecutorService. It is complex to configure a thread pool, especially when the principle of thread pool is not very clear. Therefore, some static factory methods are provided on the tool class Executors to generate some common thread pools, as shown below:
Newsinglethreadexecution: create a single threaded thread pool. This thread pool has only one thread working, which is equivalent to a single thread executing all tasks in series. If the only thread ends abnormally, a new thread will replace it. This thread pool ensures that all tasks are executed in the order they are submitted.

/**
 * @description Thread pool creation: newSingleThreadExecutor() single thread pool
 *                        The principle is the same as that of newFixedThreadPool(), except that the number of threads and the maximum number of threads are set to 1
 */
public class SingleThreadExecutor {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new FixedThreadPoolExecutor.Task());
        }
    }
}

newFixedThreadPool: creates a fixed size thread pool. Each time a task is submitted, a thread is created until the thread reaches the maximum size of the thread pool. Once the size of the thread pool reaches the maximum, it will remain unchanged. If a thread ends due to execution exception, the thread pool will supplement a new thread.

/**
 * @description Thread pool creation: newFixedThreadPool() custom thread count thread pool
 *              Because there is no upper limit on the unbounded queue of the LinkedBlockingQueue, when there are more and more requests and they cannot be processed in time,
 *              That is, when requests are stacked, it will easily occupy a lot of memory, which may lead to OOM.
 */
public class FixedThreadPoolExecutor {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Task());
        }
        // Manually close the thread
        executorService.shutdown();
    }
    static class Task implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
}

newCachedThreadPool: create a cacheable thread pool. If the size of the thread pool exceeds the threads required for processing tasks, some idle threads (not executing tasks for 60 seconds) will be recycled. When the number of tasks increases, this thread pool can intelligently add new threads to process tasks. This thread pool does not limit the size of the thread pool. The size of the thread pool completely depends on the maximum thread size that the operating system (or JVM) can create.

/**
 * @description Thread pool creation: newCachedThreadPool() caches the thread pool
 *                        Features: SynchronousQueue unbounded thread pool, with the function of automatically reclaiming redundant threads
 *                        Disadvantages: maxPoolSize is integer MAX_ Value, may create a very large number of threads, and even cause OOM.
 */
public class CachedThreadPoolExecutor {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new FixedThreadPoolExecutor.Task());
        }
    }
}

newScheduledThreadPool: create an unlimited thread pool. This thread pool supports the need to execute tasks regularly and periodically.

/**
 * @description Thread pool creation: newScheduledThreadPool() supports thread pools for timed and periodic task execution
 */
public class ScheduledThreadPoolExecutor {
    public static void main(String[] args) throws InterruptedException {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        //scheduledExecutorService.schedule(new FixedThreadPoolExecutor.Task(), 1, TimeUnit.SECONDS);
        // Every second
        scheduledExecutorService.scheduleAtFixedRate(new FixedThreadPoolExecutor.Task(), 1 , 1, TimeUnit.SECONDS);
        Thread.sleep(5000);
        scheduledExecutorService.shutdown();
    }
}

Newsinglethreadexecution: create a single threaded thread pool. This thread pool supports the need to execute tasks regularly and periodically.

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CreateThreadTest06 {
    public static void main(String[] args) {
        // Create a thread pool with 5 threads
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        long threadPoolStart= System.currentTimeMillis();
        for (int i = 0; i < 5; i++) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"Thread executed......");
                }
            });
        }
        long threadPoolEnd = System.currentTimeMillis();
        System.out.println("Time to create 5 threads:"+(threadPoolEnd-threadPoolStart));
        // Destroy thread pool
        threadPool.shutdown();
    }
}

Stop thread pool

shutdown
isShutdown: the state of whether to stop or not. It is true to start running
isTerminated: whether the thread has completely stopped
awaitTerminated: whether the thread stops returning boolean within a certain period of time
stutdownNow: stop immediately and return the interrupted list.

Thread pool status

RUNNING: accept new tasks and process the tasks in the first row.
SHUTDOWN: does not accept new tasks, but processes queued tasks.
STOP: does not accept new tasks, does not process queued tasks, and interrupts ongoing tasks.
TIDYING: all tasks have been terminated. When workCont is zero, the thread will transition to TIDYING state and run the hook method of terminate().
TERMINATED: terminate() run completed.

Daemon thread

ThreadLocal

As can be seen from the name, this is called thread variable, which means that the variable filled in ThreadLocal is input into the current thread. This variable is isolated from other threads. ThreadLocal creates a copy of the variable in each thread, so each thread can access its own internal copy variable.
Function: isolate an object that needs to be used between threads (each thread has its own independent object) and easily obtain the object in any method.
Usage scenario:

1. When transferring objects across layers, using ThreadLocal can avoid multiple transfers and break the constraints between layers.
2. Data isolation between threads
3. Conduct transaction operation, which is used to store thread transaction information.
4. Database connection, Session management.

Scenario example 1: each thread needs an exclusive object (usually tool classes, such as SimpleDateFormat and Random)

/**
 * @description threadLocal Usage 1: SimpleDateFormat
 *              Using ThreadLocal, each thread is allocated its own SimpleDateFormat object to ensure thread safety and efficient use of memory
 *
 *              Usage scenario:
 *              1.Initialize the object when ThreadLocal get s it for the first time, rewrite the initialValue() method and return value, and the object initialization time can be controlled by us
 *              2.If the generation time of the object to be saved in ThreadLocal is not under our control, use ThreadLocal Set is directly put into ThreadLocal for subsequent use
 *
 *              ThreadLocal Benefits:
 *              1.Thread safety achieved
 *              2.No locking is required to improve execution efficiency
 *              3.Make more efficient use of memory and save the cost of creating objects
 *              4.It eliminates the cumbersome task of transmitting parameters
 */
public class ThreadLocal01 {
    private static ExecutorService executorService = Executors.newFixedThreadPool(10);
    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            int index = i;
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    String result = new ThreadLocal01().data(index);
                    System.out.println(result);
                }
            });
        }
        executorService.shutdown();
    }
    public String data(int index){
        Date date = new Date(1000 * index);
        SimpleDateFormat simpleDateFormat = ThreadSafeSimpleDateFormat.threadLocal2.get();
        return simpleDateFormat.format(date);
    }
}
class ThreadSafeSimpleDateFormat {
    public static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        }
    };
    public static ThreadLocal<SimpleDateFormat> threadLocal2 = ThreadLocal.withInitial(()-> new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));
}

Scenario example 2: global variables need to be maintained in each thread (such as obtaining user information in the interceptor) so that different methods can be used directly to avoid the trouble of parameter transmission

/**
 * @description threadLocal Usage 2: global variables need to be maintained in each thread, which can be used directly by different methods to avoid the trouble of parameter passing
 *                               To prevent memory leakage, remove the ThreadLocal value when it is used up
 */
public class ThreadLocal02 {
    public static void main(String[] args) {
        Server1 server1 = new Server1();
        server1.process();
    }
}
class Server1{
    public void process(){
        User user = new User("Zhang San");
        UserContextHolder.userThreadLocal.set(user);
        Server2 server2 = new Server2();
        server2.process();
    }
}
class Server2{
    public void process(){
        User user = UserContextHolder.userThreadLocal.get();
        System.out.println("Server2 Get user name:" + user.getName());
        UserContextHolder.userThreadLocal.remove();
        User newUser = new User("Li Si");
        UserContextHolder.userThreadLocal.set(newUser);
        Server3 server3 = new Server3();
        server3.process();
    }
}
class Server3{
    public void process(){
        User user = UserContextHolder.userThreadLocal.get();
        System.out.println("Server3 Get new user name:" + user.getName());
        // Be sure to remove after use
        UserContextHolder.userThreadLocal.remove();
    }
}
class User{
    private String name;
    public String getName() {
        return name;
    }
    public User(String name) {
        this.name = name;
    }
}
class UserContextHolder{
    public static ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
}

Basic state of thread

The life cycle of a Java thread can only be one of six different states. At a given time, a thread can only be in one of them.

1. New state: the initial state. The thread has been built, but the start() method has not been called yet
2. Runnable state: running state. java threads collectively refer to the ready and running states of the operating system as "running".
3. Blocked status: blocking status, indicating thread blocking and locking.
4. Waiting state: waiting state, indicating that the thread enters the waiting state. Entering this state indicates that the thread needs to wait for other threads to make some specific actions or notifications.
5. TIME WAITING status: timeout WAITING status. This status is different from WAITING. It can return at the specified time
6. TIME WAITING status: termination status, indicating that the current thread has completed execution

In its own life cycle, Java threads are not in a fixed state, but switch between different states with the execution of code. The state transition of Java threads is shown in the following figure:

After the thread is created, the start() method is invoked to start running. After the thread executes the wait() method, the thread enters the wait state. The thread entering the waiting state can only return to the running state by relying on the notification of other threads, and the timeout waiting state is equivalent to adding the timeout limit on the basis of the waiting state, that is, it will return to the running state when the timeout time reaches. When a thread calls the synchronization method, the thread will enter the blocking state without obtaining the lock. The thread will enter the termination state after executing the run() method of Runnable.

Example test:

public class ThreadState {
    public static void main(String[] args) {
        new Thread(new TimeWaiting(),"TimeWaitingThread").start();
        new Thread(new Waiting(),"WaitingThread").start();

        new Thread(new Blocked(),"Blocked-1").start();
        new Thread(new Blocked(),"Blocked-2").start();
    }
    // This thread keeps sleeping
    static class TimeWaiting implements Runnable{
        @Override
        public void run() {
            while (true){
                SleepUtils.second(2000);
            }
        }
    }
    // This thread is in waiting Wait on class
    static class Waiting implements Runnable{
        @Override
        public void run() {
            while (true){
                synchronized (Waiting.class){
                    try {
                        Waiting.class.wait();
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    // The thread is in blocked Class instance, the lock will not be released
    static class Blocked implements Runnable{
        @Override
        public void run() {
            synchronized (Blocked.class){
                while (true){
                    SleepUtils.second(1000);
                }
            }
        }
    }
}

Code 2 (run in the same folder)

import java.util.concurrent.TimeUnit;

public class SleepUtils {
    public static final void second(long second){
        try {
            TimeUnit.SECONDS.sleep(second);
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

Run the code, open the terminal or command prompt, type "jps", and the output is as follows:

Then you can see that the process ID corresponding to the running instance is 177428, then enter jstack 17748 in the command line terminal, and some terminals output as follows:



Stopped thread

There are several ways for a thread to exit while running:

1. Use the exit flag to make the thread exit normally, that is, when the run method is executed, the thread terminates.
2. Use the stop method to force exit, but this method is not recommended
3. Interrupt a thread using the interrupt method

public class ThreadTest extends Thread{
    volatile boolean stop = false;
    public void run(){
        while (!stop){
            System.out.println(getName()+"The thread is running......");
            try{
                sleep(1000);
            }catch (InterruptedException e){
                System.out.println("wake up from block ......");
                stop = true;
                e.printStackTrace();
            }
        }
        System.out.println(getName()+"The thread has exited......");
    }
}
class InterruptThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        ThreadTest threadTest = new ThreadTest();
        System.out.println("The thread starts running......");
        threadTest.start();
        Thread.sleep(3000);
        System.out.println("appear Interrupt The thread for is:"+threadTest.getName());
        threadTest.stop = true;
        threadTest.interrupt();
        Thread.sleep(3000);
        System.out.println("The main thread has stopped......");
    }
}

The operation result is:

The difference between the start() method and the run() method

Each Thread completes its operation through the method run() corresponding to a specific Thread object. The method run() is called the Thread body. Start a Thread by calling the start() method of Thread class;

start() method to start a Thread, which really realizes multi-threaded operation. At this time, you can directly continue to execute the following code without waiting for the execution of the body code of the run() method; At this time, the Thread is in a ready state and is not running. Then, the Thread class calls the method run() to complete its running state;

The run() method is called the thread body. It contains the contents of the thread to be executed. The thread enters the running state and starts running the code in the run function. The run method ends and this thread terminates. The CPU then schedules other threads. The run() method is in this thread. It is only a function of the thread, not multithreaded. If you directly call run(), it is actually equivalent to calling an ordinary function. If you directly use the run() method, you must wait for the execution of the run() method to execute the following code. Therefore, there is only one execution path, and there is no thread feature at all. Therefore, you should use the start() method instead of the run() method when multithreading.

The difference between sleep() method and wait() method

First, the sleep() method can be used anywhere, while the wait() method can only be run in a synchronization method or synchronization block. For the sleep() method, we first need to know that the method belongs to the Thread class. The wait() method belongs to the Object class. The sleep() method causes the program to suspend execution for the specified time and give up the CPU to other threads (other CPUs can perform other tasks), but its monitoring status remains the holder. When the specified time expires, it will automatically resume running status. Note that during the call to the sleep() method, the Thread will not release the Object lock. When the wait() method is called, the current Thread will temporarily exit the synchronization resource lock, give up the Object lock and enter the waiting lock pool waiting for this Object. Only after the notify() method is called for this Object, the Thread enters the Object lock pool to obtain the Object lock and enter the running state.

The difference between Runnable interface and Callable interface

First, check the source code of the Runnable interface. Anyway, there is only one method in the Runnable interface. The return value of the run() method in the Runnable interface is void. All it does is purely execute the code in the run() method;

Secondly, check the source code of the Callable interface. The call() method in the Callable interface has a return value. It is a generic type, which can be used to obtain the results of asynchronous execution in combination with Future and FutureTask.

This is actually very meaningful. Firstly, compared with single thread operation, multi-threaded operation has many complex and difficult problems, because multi-threaded operation is full of unknowns, such as whether a thread runs, how long it runs, whether the data expected by the thread has been assigned, but the generic Future/FutureTask of Callable interface can obtain the results of multi-threaded operation, You can cancel the task of the thread when the waiting time is too long and the required data is not obtained. This is an advantage.

Thread safety

What is thread safety? When multiple threads run the same code, what happens if different results are produced? It's like an eagle hatched out of an egg laid by a chicken at home. It doesn't seem appropriate, so thread safety. In a word, when multiple threads run the same code, they won't produce different results. That is, there are multiple threads running in the process where the code is located, and these threads may run this code at the same time. If the result of each run is the same as that of a single thread, and the values of other variables are the same as expected, it is thread safe.

In a multithreaded environment, when all threads do not share data, that is, they are private members, it must be thread safe. Because there is no shared data, threads do not affect each other. However, this situation is rare. In most cases, data sharing is required, and appropriate synchronization control is required. Thread safety generally involves synchronized, that is, a piece of code can only have one thread to operate at the same time, otherwise the intermediate process may produce non prefabricated results.

Take a simple example. Originally, you had 1000 yuan in the bank, and you wanted to withdraw 1500 yuan. You went to the bank to handle two businesses, withdrawal and deposit. If your two businesses were carried out simultaneously, could you not withdraw your money at this time? Because your bank has only 1000 yuan. You can't withdraw 1500 yuan, but your money can be deposited. The money can't be withdrawn. Is the process of going to the bank to handle business useless? Because it didn't run successfully.

Ensure thread safety

Through reasonable time scheduling, the access conflict of shared resources can be avoided. In addition, in the design of parallel tasks, appropriate strategies can be used to ensure that there are no shared resources between tasks, and a rule can be designed to ensure that a client's computing work and data access will only be completed by one thread or one worker, rather than assigning a client's computing work to multiple threads.
Ways to ensure multithreading safety:

Lock the non secure code
Using thread safe classes
In the case of multithreading concurrency, the variables shared by threads are changed to method level local variables

Thread safety is embodied in three aspects:

Atomicity: provides mutually exclusive access. Only one thread can operate on data at a time (atomic, synchronized);
Visibility: a thread's modification of main memory can be seen by other threads in time (synchronized and volatile);
Orderliness: one thread observes the execution order of instructions in other threads. Due to the reordering of instructions, the observation result is generally disordered (happensbefore principle)

shared data

The first is to encapsulate the shared data into an object and pass the object where the shared data is located to different Runnable.

The second method: take these Runnable objects as the internal class of a class, the shared data as the member variables of the external class, and assign the operation of shared data to the external class to complete, so as to realize the mutual exclusion and communication of operating shared data, and the method of operating the external class as the Runnable of the internal class to realize the operation of data.

class shareData {
    private int x = 0;
    public synchronized void addX(){
        x++;
        System.out.println("X++: "+x);
    }
    public synchronized void subX(){
        x--;
        System.out.println("X--: "+x);
    }
}
public class ThreadsVisitData{
    public static shareData share = new shareData();
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    share.addX();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    share.subX();
                }
            }
        }).start();
    }
}

Keywords: Java Back-end

Added by porko2004 on Thu, 06 Jan 2022 04:11:14 +0200