In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-23 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article introduces the relevant knowledge of "how to understand Future&FutureTask in concurrent programming". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
Preface
There are four main ways to implement Java threads:
Inherit the Thread class
Implement the Runnable interface
Implement the Callable interface to create Thread threads through the FutureTask wrapper
Use ExecutorService, Callable, and Future to implement multithreading with returned results.
The first two methods have no return value after execution, and the latter two methods have a return value.
Callable and Runnable interface
Runnable interface
/ / the class that implements the Runnable interface will be executed by Thread, indicating that a basic task public interface Runnable {/ / run method is all its contents, that is, the actual task public abstract void run ();}
No return value
The run method does not return a value, and although there are some other methods that can achieve worthwhile results, such as writing log files or modifying shared variables, they are not only error-prone, but also inefficient.
Cannot throw exception
Public class RunThrowExceptionDemo {/ * ordinary methods can throw an exception in the method signature * * @ throws IOException * / public void normalMethod () throws IOException {throw new IOException () } checked Exception cannot be thrown within the class RunnableImpl implements Runnable {/ * run method unless try catch is used for processing * / @ Override public void run () {try {throw new IOException ();} catch (IOException e) {e.printStackTrace () }}
You can see that the ordinary method normalMethod can throw an exception on the method signature, so that the upper layer interface can catch the exception for handling, but for the class that implements the Runnable interface, the run method cannot throw checked Exception, so it can only be handled with try catch within the method, so the upper layer cannot know the exception in the thread.
Design leads to
In fact, the main reason for these two defects is the run method designed by the Runnable interface, which already specifies that the return type of the run () method is void, and this method does not declare to throw any exceptions. Therefore, when implementing and overriding this method, we can change neither the return value type nor the description of the exception thrown, because the syntax does not allow these changes when implementing the method.
Why is Runnable designed like this?
It doesn't help to assume that the run () method can return a return value, or throw an exception, because there is no way to catch and handle it on the outer layer, because the classes that call the run () method (such as the Thread class and thread pool) are provided directly by Java, not written by us. So even if it can have a return value, it is very difficult for us to use this return value, and the Callable interface is to solve these two problems.
Callable interface
Public interface Callable {/ / returns the interface, or throws an exception V call () throws Exception;}
You can see that the Callable and Runnable interfaces are actually similar, with only one method, that is, the thread task execution method, except that the call method has a return value and declares throws Exception.
The differences between Callable and Runnable
Method name: the execution method specified by Callable is call (), while the execution method specified by Runnable is run ()
Return value: the task of Callable has a return value after execution, while the task of Runnable has no return value
Throw an exception: the call () method can throw an exception, while the run () method cannot throw a checked exception
With Callable, there is a Future interface. Through Future, you can know the execution of the task, or cancel the execution of the task, and you can also obtain the results of the execution of the task. These functions can not be done by Runnable, and the function of Callable is more powerful than Runnable.
Future interface
The role of Future
To put it simply, the thread is used to achieve the effect of asynchronism, and the return value of the child thread can be obtained at the same time.
For example, when doing a certain operation, the operation process may be time-consuming, sometimes to check the database, or heavy calculations, such as compression, encryption, etc., in this case, if we have been waiting for the method to return, it is obviously unwise, and the efficiency of the overall program will be greatly reduced.
We can put the operation process to the child thread to execute, and then control the calculation process executed by the child thread through Future, and finally get the calculation result. In this way, the running efficiency of the whole program can be improved, which is a kind of asynchronous thought.
Future's method
There are five methods for the Future API. The source code is as follows:
Public interface Future {/ * attempted to cancel the task and failed if the task was completed, cancelled, or could not be cancelled for other reasons. * 1. If the task has not started, the task should not run * 2. If the task has already started, the parameter mayInterruptIfRunning determines whether the thread executing the task should be interrupted. This is just an attempt to terminate the task. If mayInterruptIfRunning is true, the thread executing the task will be immediately interrupted and true will be returned. If mayInterruptIfRunning is false, true will be returned without interrupting the task thread. * 3. After calling this method, all future calls to the isDone method will return true. * 4. If this method returns true, it will return true to isCancelled later. * / boolean cancel (boolean mayInterruptIfRunning); / * determine whether the task has been cancelled, and return true * / boolean isCancelled () if cance () is called; / * return ture if the task is completed * Task completion includes normal termination, exception, and cancellation. In these cases, true * / boolean isDone () is returned; / * the thread blocks until the task is completed, and the result is returned * if the task is cancelled, CancellationException is thrown * if the current thread is interrupted, InterruptedException is thrown * when an exception occurs in the execution of the task, ExecutionException * / V get () throws InterruptedException, ExecutionException is thrown / * Thread blocks for a certain amount of time to wait for the task to be completed, and returns the task execution result. If it times out, TimeoutException * / V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;} is thrown.
Get method (get results)
The main function of the get method is to obtain the results of task execution. The behavior of this method during execution depends on the state of the Callable task. The following seven situations may occur.
After the task has been executed, the execution of the get method can return immediately and get the result of the task execution.
The task has not been executed yet. For example, if we put a task into the thread pool, there may be a backlog of tasks in the thread pool. I went to get before it was my turn to execute it. In this case, it is equivalent to that the task has not yet started. When we call get, the current thread will block and return the result until the task is completed.
The task is in progress, but the execution process is long, so when I went to get, it was still in the process of execution. Calling the get method in this case also blocks the current thread until the result is returned after the task has been executed.
When an exception is thrown during the execution of a task, when we call get, we will throw an ExecutionException exception. No matter what type of exception is thrown when we execute the call method, the exception we get when we execute the get method is ExecutionException.
The task is cancelled, and if the task is cancelled, we throw a CancellationException when we use the get method to get the result.
The task is interrupted, and if the task is interrupted by the current thread, we throw an InterruptedException when we use the get method to get the result.
Task timeout, we know that the get method has an overloaded method, that is, with delay parameter. After calling the get method with delay parameter, if the call method completes the task smoothly within the specified time, then get will return normally; but if the task has not been completed at the specified time, the get method will throw a TimeoutException, which means that the timeout has occurred.
Reference example:
Package com.niuh.future; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class FutureDemo {public static void main (String [] args) {ExecutorService executorService = Executors.newSingleThreadExecutor (); Future future = executorService.submit (new FutureTask ()) Try {Integer res = future.get (2000, TimeUnit.MILLISECONDS); System.out.println ("Fusion thread return value:" + res);} catch (InterruptedException e) {e.printStackTrace ();} catch (ExecutionException e) {e.printStackTrace ();} catch (TimeoutException e) {e.printStackTrace () }} static class FutureTask implements Callable {@ Override public Integer call () throws Exception {Thread.sleep (new Random () .nextInt (3000)); return new Random () .nextInt (10);}
IsDone method (to determine whether the execution is complete)
The isDone () method, which is used to determine whether the current task has been completed
Package com.niuh.future; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class FutureIsDoneDemo {public static void main (String [] args) {ExecutorService executorService = Executors.newSingleThreadExecutor (); Future future = executorService.submit (new FutureTask ()) Try {for (int I = 0; I
< 3; i++) { Thread.sleep(1000); System.out.println("线程是否完成:" + future.isDone()); } Integer res = future.get(2000, TimeUnit.MILLISECONDS); System.out.println("Future 线程返回值:" + res); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } } static class FutureTask implements Callable { @Override public Integer call() throws Exception { Thread.sleep(2000); return new Random().nextInt(10); } } } 执行结果: 线程是否完成:false 线程是否完成:false 线程是否完成:true Future 线程返回值:9 可以看到前两次 isDone 方法的返回结果是 false,因为线程任务还没有执行完成,第三次 isDone 方法的返回结果是 ture。 注意:这个方法返回 true 则代表执行完成了,返回 false 则代表还没完成。但返回 true,并不代表这个任务是成功执行的,比如说任务执行到一半抛出了异常。那么在这种情况下,对于这个 isDone 方法而言,它其实也是会返回 true 的,因为对它来说,虽然有异常发生了,但是这个任务在未来也不会再被执行,它确实已经执行完毕了。所以 isDone 方法在返回 true 的时候,不代表这个任务是成功执行的,只代表它执行完毕了。 我们将上面的示例稍作修改再来看下结果,修改 FutureTask 代码如下: static class FutureTask implements Callable { @Override public Integer call() throws Exception { Thread.sleep(2000); throw new Exception("故意抛出异常"); } } 执行结果: 虽然抛出了异常,但是 isDone 方法的返回结果依然是 ture。 这段代码说明了: 即便任务抛出异常,isDone 方法依然会返回 true。 虽然抛出的异常是 IllegalArgumentException,但是对于 get 而言,它抛出的异常依然是 ExecutionException。 虽然在任务执行到2秒的时候就抛出了异常,但是真正要等到我们执行 get 的时候,才看到了异常。 cancel方法(取消任务的执行) 如果不想执行某个任务了,则可以使用 cancel 方法,会有以下三种情况: 第一种情况最简单,那就是当任务还没有开始执行时,一旦调用 cancel,这个任务就会被正常取消,未来也不会被执行,那么 cancel 方法返回 true。 第二种情况也比较简单。如果任务已经完成,或者之前已经被取消过了,那么执行 cancel 方法则代表取消失败,返回 false。因为任务无论是已完成还是已经被取消过了,都不能再被取消了。 第三种情况比较特殊,就是这个任务正在执行,这个时候执行 cancel 方法是不会直接取消这个任务的,而是会根据我们传入的参数做判断。cancel 方法是必须传入一个参数,该参数叫作 mayInterruptIfRunning,它是什么含义呢?如果传入的参数是 true,执行任务的线程就会收到一个中断的信号,正在执行的任务可能会有一些处理中断的逻辑,进而停止,这个比较好理解。如果传入的是 false 则就代表不中断正在运行的任务,也就是说,本次 cancel 不会有任何效果,同时 cancel 方法会返回 false。 参考示例: package com.niuh.future; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class FutureCancelDemo { static ExecutorService executorService = Executors.newSingleThreadExecutor(); public static void main(String[] args) { // 当任务还没有开始执行 // demo1(); // 如果任务已经执行完 // demo2(); // 如果任务正在进行中 demo3(); } private static void demo1() { for (int i = 0; i < 1000; i++) { executorService.submit(new FutureTask()); } Future future = executorService.submit(new FutureTask()); try { boolean cancel = future.cancel(false); System.out.println("Future 任务是否被取消:" + cancel); String res = future.get(2000, TimeUnit.MILLISECONDS); System.out.println("Future 线程返回值:" + res); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } finally { executorService.shutdown(); } } private static void demo2() { Future future = executorService.submit(new FutureTask()); try { Thread.sleep(1000); boolean cancel = future.cancel(false); System.out.println("Future 任务是否被取消:" + cancel); } catch (InterruptedException e) { e.printStackTrace(); } finally { executorService.shutdown(); } } private static void demo3() { Future future = executorService.submit(new FutureInterruptTask()); try { Thread.sleep(1000); boolean cancel = future.cancel(true); System.out.println("Future 任务是否被取消:" + cancel); } catch (InterruptedException e) { e.printStackTrace(); } finally { executorService.shutdown(); } } static class FutureTask implements Callable { @Override public String call() throws Exception { return "正常返回"; } } static class FutureInterruptTask implements Callable { @Override public String call() throws Exception { while (!Thread.currentThread().isInterrupted()) { System.out.println("循环执行"); Thread.sleep(500); } System.out.println("线程被中断"); return "正常返回"; } } } 这里,我们来分析下第三种情况(任务正在进行中),当我们设置 true 时,线程停止 循环执行 循环执行 Future 任务是否被取消:true 当我们设置 false 时,任务虽然也被取消成功,但是线程依然执行。 循环执行 循环执行 Future 任务是否被取消:true 循环执行 循环执行 循环执行 循环执行 ...... 那么如何选择传入 true 还是 false 呢? 传入 true 适用的情况是,明确知道这个任务能够处理中断。 传入 false 适用于什么情况呢?如果我们明确知道这个线程不能处理中断,那应该传入 false。我们不知道这个任务是否支持取消(是否能响应中断),因为在大多数情况下代码是多人协作的,对于这个任务是否支持中断,我们不一定有十足的把握,那么在这种情况下也应该传入 false。如果这个任务一旦开始运行,我们就希望它完全的执行完毕。在这种情况下,也应该传入 false。 需要注意的是,虽然示例中写了 !Thread.currentThread().isInterrupted() 方法来判断中断,但是实际上并不是通过我们的代码来进行中断,而是 Future#cancel(true) 内部调用 t.interrupt 方法修改线程的状态之后,Thread.sleep 会抛出 InterruptedException 异常,线程池中会执行异常的相关逻辑,并退出当前任务。 sleep 和 interrupt 会产生意想不到的效果。 比如我们将 FutureInterruptTask 代码修改为 while(true) 形式,调用 cancel(true) 方法线程还是会被中断。 static class FutureInterruptTask implements Callable { @Override public String call() throws Exception { while (true) { System.out.println("循环执行"); Thread.sleep(500); } } } isCancelled方法(判断是否被取消) isCancelled 方法,判断是否被取消,它和 cancel 方法配合使用,比较简单,可以参考上面的示例。 Callable 和 Future 的关系 Callable 接口相比于 Runnable 的一大优势是可以有返回结果,返回结果就可以用 Future 类的 get 方法来获取 。因此,Future 相当于一个存储器,它存储了 Callable 的 call 方法的任务结果。 除此之外,我们还可以通过 Future 的 isDone 方法来判断任务是否已经执行完毕了,还可以通过 cancel 方法取消这个任务,或限时获取任务的结果等,总之 Future 的功能比较丰富。 FutureTask Future只是一个接口,不能直接用来创建对象,其实现类是FutureTask,JDK1.8修改了FutureTask的实现,JKD1.8不再依赖AQS来实现,而是通过一个volatile变量state以及CAS操作来实现。FutureTask结构如下所示: 我们来看一下 FutureTask 的代码实现: public class FutureTask implements RunnableFuture {...} 可以看到,它实现了一个接口,这个接口叫作 RunnableFuture。 RunnableFuture接口 我们来看一下 RunnableFuture 接口的代码实现: public interface RunnableFuture extends Runnable, Future { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run(); } 既然 RunnableFuture 继承了 Runnable 接口和 Future 接口,而 FutureTask 又实现了 RunnableFuture 接口,所以 FutureTask 既可以作为 Runnable 被线程执行,又可以作为 Future 得到 Callable 的返回值。 FutureTask源码分析 成员变量 /* * 当前任务运行状态 * NEW ->COMPLETING-> NORMAL (normal end, return result) * NEW-> COMPLETING-> EXCEPTIONAL (return abnormal result) * NEW-> CANCELLED (task cancelled, no result) * NEW-> INTERRUPTING-> INTERRUPTED (task interrupted, no result) * / private volatile int state; private static final int NEW = 0; / / New 0 private static final int COMPLETING = 1; / / executing 1 private static final int NORMAL = 2 / / normal 2 private static final int EXCEPTIONAL = 3; / / exception 3 private static final int CANCELLED = 4; / / cancel 4 private static final int INTERRUPTING = 5; / / interrupted 5 private static final int INTERRUPTED = 6; / / interrupted 6 / * * tasks to be performed * / private Callable callable / * * stores the execution result, which is used to obtain the result by the get () method, and may also be used by the get () method to throw an exception * / private Object outcome; / / non-volatile, protected by state reads/writes / * * the thread that executes the task Callable; the waiting queue of * / private volatile Thread runner; / * * stack structure, which is the top node in the stack * / private volatile WaitNode waiters
In order to better analyze the implementation of FutureTask, it is necessary to explain the various states here.
NEW: indicates a new task or a task that hasn't been completed yet. This is the initial state.
COMPLETING: when a task has been executed or an exception occurs when it is executed, but the task execution result or the reason for the exception has not been saved to the outcome field (the outcome field is used to save the task execution result, and if an exception occurs, it is used to save the exception reason), the status will be changed from NEW to COMPLETING. But this state will be relatively short and belong to the intermediate state.
NORMAL: the task has been executed and the result of the task execution has been saved to the outcome field, and the state is transitioned from COMPLETING to NORMAL. This is the final state.
EXCEPTIONAL: after an exception occurs in the execution of the task and the reason for the exception has been saved in the outcome field, the state changes from COMPLETING to EXCEPTIONAL. This is the final state.
CANCELLED: the user calls the cancel (false) method to cancel the task without interrupting the task execution thread when the task has not started or has already started but has not been completed. At this time, the state changes from NEW to CANCELLED. This is the final state.
INTERRUPTING: when a task has not started or has been executed but has not been completed, the user calls the cancel (true) method to cancel the task and interrupts the task execution thread but before interrupting the task execution thread, the state changes from NEW to INTERRUPTING. This is an intermediate state.
INTERRUPTED: the state changes from INTERRUPTING to INTERRUPTED after calling interrupt () to interrupt the task execution thread. This is the final state.
It is important to note that all states with values greater than COMPLETING indicate that the task has completed execution (normal task execution, task execution exception, or task cancellation).
Construction method
/ / Callable construction method public FutureTask (Callable callable) {if (callable = = null) throw new NullPointerException (); this.callable = callable; this.state = NEW; / / ensure visibility of callable} / / Runnable construction method public FutureTask (Runnable runnable, V result) {this.callable = Executors.callable (runnable, result); this.state = NEW; / / ensure visibility of callable}
The constructor of Runnable has only one purpose, which is to convert the input parameter into RunnableAdapter through Executors.callable, mainly because Callable is more functional than Runnable, and Callable has a return value, while Runnable does not.
/ * A callable that runs given task and returns given result * / static final class RunnableAdapter implements Callable {final Runnable task; final T result; RunnableAdapter (Runnable task, T result) {this.task = task; this.result = result;} public T call () {task.run (); return result;}}
This is a typical adaptation model. If we want to adapt Runnable to Callable, we must first implement the interface of Callable, and then call the method of the adapted object (Runnable) in the call method of Callable.
Inner class
Static final class WaitNode {volatile Thread thread; volatile WaitNode next; WaitNode () {thread = Thread.currentThread ();}}
Run method
/ * the run method can be called directly * or you can open a new thread call * / public void run () {/ / status is not a task creation, or the current task is already being executed by a thread. Return if (state! = NEW | |! U.compareAndSwapObject (this, RUNNER, null, Thread.currentThread ()) return; try {Callable c = callable) / / Callable is not empty and if has been initialized (c! = null & & state = = NEW) {V result; boolean ran; try {/ / call execution result = c.call (); ran = true } catch (Throwable ex) {result = null; ran = false;// execution failed / / return value (COMPLETING) and status value (EXCEPTIONAL) setException (ex) are set by CAS algorithm } / / successfully assigned result to outcome set (result) through CAS (UNSAFE) setting return value (COMPLETING) and status value (NORMAL) if (ran) / / }} finally {/ / runner must be non-null until state is settled to / / prevent concurrent calls to run () / / set task runner to null to avoid concurrent calls to the run () method runner = null / / state must be re-read after nulling runner to prevent / / leaked interrupts / / must re-read the task status to avoid unreachable (leaked) interrupts int s = state; / / ensure that running tasks can receive interrupt instructions if (s > = INTERRUPTING) handlePossibleCancellationInterrupt (s);}}
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
The run method does not return a value, and by assigning a value to the outcome property (set (result)), you can get the return value from the outcome property when get.
Both FutureTask constructors are eventually converted into Callable, so when the run method is executed, you only need to execute the call method of Callable. When executing the c.call () code, if the input parameter is Runnable, the call path is c.call ()-> RunnableAdapter.call ()-> Runnable.run (), and if the input parameter is Callable, call it directly.
SetException (Throwable t) method
/ / when an exception occurs, set the return value to outcome (= COMPLETING) and update the task status (EXCEPTIONAL) protected void setException (Throwable t) {/ / call the CAS algorithm encapsulated by the UNSAFE class, and set the value if (UNSAFE.compareAndSwapInt (this, stateOffset, NEW, COMPLETING)) {outcome = t; UNSAFE.putOrderedInt (this, stateOffset, EXCEPTIONAL); / / final state / / wakes up the thread finishCompletion () that is blocked due to waiting for the return value;}}
Due to the limited space, please see the article extension link for more source code parsing.
The use of Future
FutureTask can be used to get the execution result asynchronously or to cancel the execution of a task. By passing the task of Runnable or Callable to FutureTask, call its run method directly or put it into the thread pool for execution, and then you can obtain the execution result asynchronously through the get method of FutureTask. Therefore, FutureTask is very suitable for time-consuming computing, and the main thread can obtain the result after completing its own task. In addition, FutureTask ensures that even if the run method is called multiple times, it will only execute the Runnable or Callable task once, or cancel the execution of the FutureTask through cancel, and so on.
Usage scenarios for FutureTask to perform multitasking computing
With FutureTask and ExecutorService, computing tasks can be submitted in a multi-threaded manner, and the main thread continues to execute other tasks, and when the main thread needs the calculation results of the child threads, the execution results of the child threads are obtained asynchronously.
/ / the task completes normally, set the return value to outcome (= COMPLETING) and update the task status (= NORMAL) protected void set (V v) {if (UNSAFE.compareAndSwapInt (this, stateOffset, NEW, COMPLETING)) {outcome = v; UNSAFE.putOrderedInt (this, stateOffset, NORMAL); / / final state finishCompletion ();}}
Execution result:
Generate sub-thread calculation task: 0 generate sub-thread calculation task: 1 generate sub-thread calculation task: 2 generate sub-thread calculation task: 3 generate sub-thread calculation task: 4 generate sub-thread calculation task: 5 generate sub-thread calculation task: 6 generate sub-thread calculation task: 7 generate sub-thread calculation task: 8 generate sub-thread calculation task: 9 all calculation tasks have been submitted The main thread goes on to do other things! Subthread calculation task: 0 execution completed! Sub-thread calculation task: 1 execution completed! Sub-thread calculation task: 3 execution completed! Sub-thread calculation task: 4 execution completed! Sub-thread calculation task: 2 execution completed! Sub-thread calculation task: 5 execution completed! Sub-thread calculation task: 7 execution completed! Subthread calculation task: 9 execution completed! Subthread calculation task: 8 execution completed! Sub-thread calculation task: 6 execution completed! The total result of multitasking calculation is: 990
FutureTask ensures that tasks are executed only once in a highly concurrent environment
In many high concurrency environments, we often only need to perform certain tasks once. The features of this usage scenario FutureTask are just right. For example, suppose there is a connection pool with key. When key exists, the object corresponding to key is returned directly; when key does not exist, a connection is created. For such an application scenario, the usual method is to use a Map object to store the corresponding relationship between key and connection pool. The typical code is shown below:
/ / remove all waiting threads and signal, call done (), and empty task callable private void finishCompletion () {/ / assert state > COMPLETING; for (WaitNode Q; (Q = waiters)! = null;) {if (UNSAFE.compareAndSwapObject (this, waitersOffset, Q, null)) {/ / cycle to wake up blocking threads until the blocking queue is listed as empty for ( ) {Thread t = q.thread; if (t! = null) {q.thread = null; LockSupport.unpark (t);} WaitNode next = q.next / / until the blocking queue is empty, jump out of the loop if (next = = null) break; q.next = null; / / unlink to help gc to facilitate gc to recycle Q = next;} break;}} done () at the appropriate time. Callable = null; / / to reduce footprint}
In the above example, we ensured thread safety in highly concurrent environments by locking, and ensured that the connection was created only once, at the expense of performance. In the case of switching to ConcurrentHash, the locking operation can almost be avoided, and the performance is greatly improved.
Package com.niuh.future; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.concurrent.ConcurrentHashMap; / * * @ description: when ConcurrentHash is used, locking can almost be avoided, and the performance is greatly improved. *
* however, in the case of high concurrency, it is possible that Connection is created multiple times. * Why? Because creating a Connection is a time-consuming operation, suppose that multiple threads pour into the getConnection method and find that the corresponding key for key does not exist. * then all the incoming threads begin to execute conn=createConnection (), but only one thread can insert connection into the map. * but in this way, the connection created by other threads is worthless and a waste of system overhead. * / public class FutureTaskConnection2 {private static ConcurrentHashMap connectionPool = new ConcurrentHashMap (); public static Connection getConnection (String key) {Connection connection = connectionPool.get (key); if (connection = = null) {connection = createConnection (); / / judging whether a thread preemptively inserts Connection returnConnection = connectionPool.putIfAbsent (key, connection) based on the returned value of putIfAbsent If (returnConnection! = null) {connection = returnConnection;}} else {return connection;} return connection;} private static Connection createConnection () {try {return DriverManager.getConnection (");} catch (SQLException e) {e.printStackTrace () } return null;}}
However, in the case of high concurrency, it is possible for Connection to be created multiple times. Why?
Because creating a Connection is a time-consuming operation, suppose that multiple threads pour into the getConnection method and find that the key corresponding to the key does not exist, so all the incoming threads begin to execute conn=createConnection (), but only one thread can insert the connection into the map. But in this way, the connection created by other threads is of little value and a waste of system overhead.
The most important problem to be solved at this time is that when key does not exist, the action of creating Connection (conn=createConnection ();) can be executed after connectionPool.putIfAbsent (). This is the right time for FutureTask to play a role. The modification code based on ConcurrentHashMap and FutureTask is as follows:
Package com.niuh.future; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; / * * @ description: FutureTask ensures that the task is executed only once in a highly concurrent environment * the most important problem to be solved at this time is to create the Connection action (conn=createConnection ()) when key does not exist. ) * can be executed after connectionPool.putIfAbsent (), this is the right time for FutureTask to play a role. * the transformation code based on ConcurrentHashMap and FutureTask is as follows: * / public class FutureTaskConnection3 {private static ConcurrentHashMap connectionPool = new ConcurrentHashMap (); public static Connection getConnection (String key) {FutureTask connectionFutureTask = connectionPool.get (key); try {if (connectionFutureTask! = null) {return connectionFutureTask.get () } else {Callable callable = new Callable () {@ Override public Connection call () throws Exception {return createConnection ()}}; FutureTask newTask = new FutureTask (callable) FutureTask returnFt = connectionPool.putIfAbsent (key, newTask); if (returnFt = = null) {connectionFutureTask = newTask; newTask.run ();} return connectionFutureTask.get ();}} catch (ExecutionException e) {e.printStackTrace () } catch (InterruptedException e) {e.printStackTrace ();} return null;} private static Connection createConnection () {try {return DriverManager.getConnection (");} catch (SQLException e) {e.printStackTrace ();} return null;}}
Callback after execution of FutureTask task
FutureTask has a method void done () that calls back when each thread executes the completion of the return result. Suppose you now need to implement each thread to proactively execute subsequent tasks after completing task execution.
Private void handlePossibleCancellationInterrupt (int s) {/ / It is possible for our interrupter to stall before getting a / / chance to interrupt us. Let's spin-wait patiently. / / spin waiting for cancle (true) to end (interrupt ends) if (s = = INTERRUPTING) while (state = = INTERRUPTING) Thread.yield (); / / wait out pending interrupt / / assert state = = INTERRUPTED; / / We want to clear any interrupt we may have received from / / cancel (true). However, it is permissible to use interrupts / / as an independent mechanism for a task to communicate with / / its caller, and there is no way to clear only the / / cancellation interrupt. / Thread.interrupted ();}
Execution result:
11 pool-1-thread-1 01 pool-1-thread-1 37.134 [Thread-0] INFO com.niuh.future.FutureTaskDemo1-the boss gave me a moon cake 11 pool-1-thread-1 01v 37.139 [pool-1-thread-1] moon cake is in production. 11PUR 01UR 37.139 [pool-1-thread-2] INFO com.niuh.future.FutureTaskDemo1-mooncake production. 11PUR 01UR 37.139 [pool-1-thread-3] INFO com.niuh.future.FutureTaskDemo1-mooncake production. 11pool-1-thread-3 01pool-1-thread-3 42.151 [pool-1-thread-2] INFO com.niuh.future.FutureTaskDemo1-No. [804] Moon cakes have been packaged 11 pool-1-thread-3] INFO com.niuh.future.FutureTaskDemo1-No. [88] Moon cakes have been packaged 11Rom 01pool-1-thread-3 INFO com.niuh.future.FutureTaskDemo1-numbered [88] Moon cakes have been packaged [pool-1-thread-1] INFO com.niuh.future.FutureTaskDemo1-numbered mooncakes have been packaged how to do concurrent programming So much for the introduction of "understanding Future&FutureTask". Thank you for your reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.