A brief discussion of Java multithreading

Java multithreading is a very important feature of the Java language, which allows programs to perform multiple tasks at the same time. Through multithreading, a program can handle multiple tasks at the same time, thereby shortening the execution time of the program. In addition, multi-threading also helps to utilize multi-core processors to better utilize the performance of computer hardware.

So how do we implement multi-threading in Java?

ThreadToday we will talk about it from the simplest Runnableto the more commonly used ones today.CompletableFuture

Thread

Threads are created in Java by creating Threadan instance of a class. The simplest way to create a thread is to extend Threadthe class and override run()the method. Write the code to be executed in run()the method, and then call the method in the program start()to start the thread. For example:

public class MyThread extends Thread {
    
    
    public void run() {
    
    
        // 线程要执行的代码
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + i + "}");
        }
    }
}

public static void main(String[] args) {
    
    
	System.out.println("start");
	MyThread myThread1 = new MyThread();
	MyThread myThread2 = new MyThread();
	myThread1.start();
	myThread2.start();
	System.out.println("end");
}

Of course, if you find it troublesome, you can directly use anonymous inner classes

		// 效果是一样的
        System.out.println("start");
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "{" + i + "}");
            }
        }).start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "{" + i + "}");
            }
        }).start();
        System.out.println("end");
       

Note here that you must not use run()methods, which will make the threads synchronized. At the same time, if you want to execute the subsequent code after the multi-threaded content is executed, then you can use join()this method, which can make the thread finish executing. Then go to perform the following operations.
Of course, you can also use it sleep()to let the main thread sleep for a period of time. Of course, this sleep time is subjective and is determined by ourselves. This method is not recommended.

Runnable

In addition, we can also use Runnableinterfaces to create threads. RunnableThe interface has only one run()method, in which you write the code to be executed, then Runnablepass the object as a parameter to Threadthe constructor of the class and call start()the method to start the thread. For example:

public class MyThread implements Runnable {
    
    
    @Override
    public void run() {
    
    
        // 线程要执行的代码
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + i + "}");
        }
    }
}

In fact, it Threadis almost the same. It is just an implementation interface and an inheritance class. If used directly, there is not much difference between the two.
However, some places will say that Runnableit is easy to realize resource sharing, but Threadit is not. However, after my testing, I think Threadresource sharing can also be realized.

test code

public class MyThread extends Thread {
    
    

    private int ticket = 5;

    @Override
    public void run() {
    
    
    	// 双检索机制——保证线程的安全
        if (ticket > 0) {
    
    
            synchronized (this) {
    
    
                if (ticket > 0) {
    
    
                    while (true) {
    
    
                        System.out.println("Thread:" + Thread.currentThread().getName() + "--Thread ticket = " + ticket--);
                        if (ticket < 0) {
    
    
                            break;
                        }
                    }
                }
            }
        }
    }

}

public class Test {
    
    
    public static void main(String[] args) {
    
    

        MyThread myThread1 = new MyThread();
        new Thread(myThread1).start();
        new Thread(myThread1).start();
        new Thread(myThread1).start();
        new Thread(myThread1).start();
        new Thread(myThread1).start();
        new Thread(myThread1).start();
    }
}

执行结果如下:
Thread:Thread-1--Thread ticket = 5
Thread:Thread-1--Thread ticket = 4
Thread:Thread-1--Thread ticket = 3
Thread:Thread-1--Thread ticket = 2
Thread:Thread-1--Thread ticket = 1
Thread:Thread-1--Thread ticket = 0

When we looked at Threadthe source code, we found that: in fact, Threadit just implements Runnablethe interface and provides more methods. So there is no difference Threadbetween and. RunnableIf there is any difference, it would be the difference between classes and interfaces, and the difference between inheritance and implementation.

Callable (with Future)

For child threads, we sometimes may have two needs:

  1. Obtain the running result of the child thread
  2. Get the running status of the child thread (success, failure, exception)

ThreadNeither and Runnabledo not meet these two requirements. RunnableThe status can be obtained but the result cannot be obtained, so it appears Callable. CallableUsed together Future, the execution result of the sub-thread can be obtained.
( FutureIt is an asynchronous calculation method in Java multi-threading, which can be used to obtain the results of asynchronous calculations during task execution)

public class MyThread implements Callable<Integer> {
    
    

    private Integer number;

    public Integer getNumber(){
    
    return number;}

    public MyThread (Integer number) {
    
    
        this.number = number;
    }

    @Override
    public Integer call() throws Exception {
    
    
        int result = 0;
        for (int i = 0; i < number; i++) {
    
    
            result ++;
            System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + result + "}");
        }
        return result;
    }
}

    public static void main(String[] args) throws Exception {
    
    
        System.out.println("start");
        MyThread myThread1= new MyThread (10);
        MyThread myThread2= new MyThread (15);
        //使用Executors工厂类创建一个简单的线程池(线程数:2)
        ExecutorService executor = Executors.newFixedThreadPool(2);
		// 将任务提交给线程池	
        Future<Integer> submit1 = executor.submit(myThread1);
        Future<Integer> submit2 = executor.submit(myThread2);
        // 获取任务的执行结果
        System.err.println(submit1.get());
        System.err.println(submit2.get());
        executor.shutdown();
        System.out.println("end");
    }

In this example, we implement MyThreadthe class, which implements Callablethe interface and overrides call()the methods. In call()the method, we simulate a long-running task and return a calculation result as the execution result. In the main method, we first create a thread pool with a fixed number of threads ExecutorService, and then MyThreadpass the instance into submitthe method to submit the task and get an Futureobject of type. By calling get()the method of this object, we can obtain the execution results of the task. Finally, we turn off the thread pool.

It should be noted that since the called Futuremethod get()is blocking, we may need to process it try-catch. Also, after completing the task, we have to close the thread pool to release the resources.

This is a simple usage Callableexample, but Futuremore complex concurrent computations can be achieved by combining multiple objects.

Future

Of course Futureit can also be used alone.
When in use Future, you can call get()a method to block waiting for the task to be executed and obtain the calculation result; you can also call isDone()a method to determine whether the task has been completed; if an exception occurs during task execution, you can get()obtain the exception information by calling the method

public static void main(String[] args) throws Exception {
    
    
        System.out.println("start");
        
        ExecutorService executor = Executors.newFixedThreadPool(2);
        Future future1 = executor.submit(() -> {
    
    
            int result = 0;
        for (int i = 0; i < number; i++) {
    
    
            result ++;
            System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + result + "}");
        }
        return result;
        });
		Future future2 = executor.submit(() -> {
    
    
            int result = 0;
        for (int i = 0; i < number; i++) {
    
    
            result ++;
            System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + result + "}");
        }
        return result;
        });
        String result1 = (String) future1.get();
        String result2 = (String) future2.get();
        System.out.println(result1);
		System.out.println(result2);
        executor.shutdown();
        System.out.println("end");
    }

In fact, it is the same as the previous method, except that call()the business logic in the method is redundant into the main code, and the coupling is higher. If only some simple codes can be used, if it is complicated, it is recommended to callableuse them together for comparison. good

CompletableFuture

CompletableFutureis a very useful feature introduced in Java 8 that allows us to handle asynchronous operations more easily, especially when these operations involve multiple stages.
CompletableFutureInherited from the Futureinterface, the calculation results can be obtained after the calculation is completed, so it also has Futurethe characteristics of . In addition, it provides some other features:

  1. Asynchronous execution and serial execution functions
  2. The observer mechanism can be triggered after the task is executed
  3. Two tasks can be combined and executed together to handle more complex asynchronous scenarios.

Nowadays, it is common to use more complex logic in development.CompletableFuture

createCompletableFuture

We can use runAsync()the method to directly execute asynchronously

CompletableFuture.runAsync(() -> {
    
    
    // 在这里执行一些耗时的操作
   
});

CompletableFutureThe result of asynchronous processing

We can use CompletableFuturethe static method supplyAsync()to create an asynchronous execution CompletableFutureand provide an lambdaexpression as its calculation generator:

CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
    
    
    // 在这里执行一些耗时的操作
    return "some result";
});

completableFuture.thenAccept(result -> {
    
    
    System.out.println("Got result: " + result);
});
// 这里可以继续执行其他任务,completableFuture将在后台继续执行并在完成后触发回调函数。

Operate two or moreCompletableFuture

If we need to wait for multiple CompletableFuturetasks to be completed before executing certain tasks, then we can use CompletableFuturestatic methods allOf()or anyOf():

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    
    
    // 在这里执行一些耗时的操作
    return "Result of future 1";
});

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    
    
    // 在这里执行一些耗时的操作
    return "Result of future 2";
});

CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2);

allFutures.thenRun(() -> {
    
    
	//这边join和get方法都可以 只是抛出异常的区别
    String result1 = future1.join();
    String result2 = future2.join();
    System.out.println("Got both results: " + result1 + " and " + result2);
});

CompletableFutureThe thread pool is used by default ForkJoinPool. If you want to define a thread pool yourself, you can create Executorsone directly. However, Executorsit is a factory class that provides some static methods for creating thread pools, not a thread pool itself, so the thread cannot be set. Detailed parameters such as: number of core threads, maximum number of threads, rejection policy, etc. If you need to set these parameters, you should ThreadPoolExecutormanually create the thread pool using the

    /**
     * public ThreadPoolExecutor(int corePoolSize,
     *                               int maximumPoolSize,
     *                               long keepAliveTime,
     *                               TimeUnit unit,
     *                               BlockingQueue<Runnable> workQueue,
     *                               ThreadFactory threadFactory,
     *                               RejectedExecutionHandler handler) {}
     * 1. corePoolSize:核心线程数;当提交的任务数大于 corePoolSize 时,线程池会自动扩容。
     *
     * 2. maximumPoolSize:线程池最大线程数;当活动线程数达到该值,并且 workQueue 队列已满,则执行拒绝策略。
     *
     * 3. keepAliveTime:线程空闲超时时间,超过该时间则进行回收。
     *
     * 4. unit:keepAliveTime 的时间单位。
     *
     * 5. workQueue:任务阻塞队列,用于存储提交但尚未执行的任务。
     *
     * 6. threadFactory:线程工厂,用于创建线程。
     *
     * 7. handler:拒绝策略,当线程数量已经达到 maximumPoolSize 并且队列已满时,采取的策略。
     */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
                10,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());

There springbootis a method that is also used very frequently - @asyncyou only need to add this annotation to the method and add it to the startup class @Enableasyncto directly implement multi-threaded exception operations. However, this multi-threaded method is still somewhat different from the one mentioned above. The difference @asyncis that when calling a method, multi-threaded asynchronous operations are performed, but the business logic in the method is still synchronous, so it can be used together normally (of ComplatableFuturecourse, you can also extract the business logic in the middle and add a @asynchahahaha) )

Java's multi-threading mechanism is an indispensable part of Java programming. Understanding and being familiar with Java's multi-threading programming capabilities will greatly improve programmers' work efficiency and programming level.

Guess you like

Origin blog.csdn.net/qq_43649799/article/details/130440008