浅谈并发线程

113 阅读5分钟

我正在参加「掘金·启航计划」

并发线程的开启

并发可以通过

实现 runable 方法

 class TaskTest1 implements Runnable{
 ​
     @Override
     public void run() {
         
     }
 }
 ​

实现callable方法

 class TaskTest implements Callable<String>{
     String username;
     String password;
 ​
     @Override
     public String call() throws Exception {
         return "task";
     }
 }

继承Thread类

 class TaskTest2 extends Thread{
 ​
     @Override
     public void run() {
 ​
     }
 }

实现开启异步的功能

一般使用callable或者实现runable的方法实现使用线程的类

并发线程的执行

不使用Executors的原因

最好不要使用

 Executors.newFixedThreadPool

因为newFixedThreadPool的等待队列可以无限长,容易造成oom异常

也不要使用newCachedThreadPool,因为其执行队列可以无限长,容易将资源占满导致oom

ThreadPoolExeutor

ThreadPoolExecutor对象

在介绍Executors创建线程池方法前先介绍一下ThreadPoolExecutor,因为这些创建线程池的静态方法都是返回ThreadPoolExecutor对象,和我们手动创建ThreadPoolExecutor对象的区别就是我们不需要自己传构造函数的参数。ThreadPoolExecutor的构造函数共有四个,但最终调用的都是同一个:

 public ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue,
                           ThreadFactory threadFactory,
                           RejectedExecutionHandler handler)

使用时一般指定五个参数就足够了

 new ThreadPoolExecutor(5,
                 10,
                 60,
                 TimeUnit.SECONDS,
                 new ArrayBlockingQueue<Runnable>(20));

new ArrayBlockingQueue(20)创建了一个最大对象为20的阻塞队列,大于该队列的请求将被丢弃(或执行拒绝逻辑)

image-20230626103403368.png

ListenableFuture

ListeningExecutorService是threadPoolExecutor线程池的一个装饰器类,他增加了当任务执行完毕时执行回调函数的功能。

ListenableFuture是Guava库中的一个接口,它扩展了Java标准库中的Future接口,提供了异步执行任务并返回结果的能力。与Java标准库中的Future不同,ListenableFuture可以注册回调函数,在任务完成时自动执行回调函数。

ListenableFuture接口中定义了以下几个方法:

  1. addListener(Runnable listener, Executor executor)方法:注册一个回调函数,在任务完成时执行该回调函数。listener参数是回调函数,executor参数是执行回调函数的Executor对象。
  2. isDone()方法:判断任务是否已经完成。
  3. get()方法:获取任务的执行结果,如果任务还未完成,则会阻塞当前线程直到任务完成。
  4. get(long timeout, TimeUnit unit)方法:获取任务的执行结果,如果任务还未完成,则会阻塞当前线程一段时间,如果在指定的时间内任务仍未完成,则会抛出TimeoutException异常。

ListenableFuture接口的实现类包括SettableFuture和ListeningExecutorService.submit()方法返回的ListenableFutureTask等。

使用ListenableFuture可以方便地实现异步执行任务并在任务完成时执行回调函数的功能,避免了手动创建线程和同步等繁琐的操作。同时,Guava库中的ListenableFuture还提供了一些其他的功能,如转换、合并等,可以进一步简化异步编程的复杂度。

 public static void TestThreadPoolExecutor(){
         TaskTest taskTest = new TaskTest();
         taskTest.username = "123";
         taskTest.password = "123";
 ​
         ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
                 10,
                 60,
                 TimeUnit.SECONDS,
                 new ArrayBlockingQueue<Runnable>(20));
         ListeningExecutorService listeningPool = MoreExecutors.listeningDecorator(threadPoolExecutor);
         ListenableFuture<String> submit = listeningPool.submit(taskTest);
         //然而这个listen方法并不能有返回值
         submit.addListener(new Runnable() {
             @Override
             public void run() {
                 System.out.println("方法submit已执行完毕");
             }
         },threadPoolExecutor);
         //addlistener方法添加一个Runable的回调函数,当submit执行完毕之后,会调用第二个参数threadPoolExecutor的线程池执行这个回调函数
     }

Future方法

ListeningExecutorService并不能带返回值,无法对线程池内参数传参,可以使用Future类进行管理

 public static void TestFuture(){
         TaskTest taskTest = new TaskTest();
         taskTest.username = "123";
         taskTest.password = "123";
 ​
         ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
                 10,
                 60,
                 TimeUnit.SECONDS,
                 new ArrayBlockingQueue<Runnable>(20));
 ​
         ListeningExecutorService listeningPool = MoreExecutors.listeningDecorator(threadPoolExecutor);
         ListenableFuture<String> future = listeningPool.submit(taskTest);
         /*
           FutureCallBack接口可以对每个任务的成功或失败单独做出响应
          */
         FutureCallback<String> futureCallback = new FutureCallback<String>() {
             @Override
             public void onSuccess(String result) {
                 System.out.println("Futures.addCallback 能带返回值:" + result);
             }
             @Override
             public void onFailure(Throwable t) {
                 System.out.println("出错,业务回滚或补偿");
             }
         };
 ​
         //为任务绑定回调接口
         Futures.addCallback(future, futureCallback, listeningPool);
     }

Futures类的addCallback()方法是Guava库中的一个方法,用于向一个Future对象添加回调函数。回调函数是一种异步编程的技术,它允许在异步计算完成后自动执行一些操作,而无需等待计算完成。

addCallback()方法接受两个参数:一个是成功回调函数,另一个是失败回调函数。当Future对象的计算成功完成时,成功回调函数会被自动执行;当计算失败时,失败回调函数会被自动执行。回调函数可以是任何实现了接口ListenableFutureCallback的类的实例。

使用addCallback()方法可以方便地处理异步计算的结果,避免了手动检查Future对象的状态并等待计算完成的繁琐操作。同时,回调函数的执行是在异步计算完成后自动执行的,不会阻塞当前线程,提高了程序的并发性能。

需要注意的是,addCallback()方法只能用于Guava库中的ListenableFuture对象,而不能用于Java原生的Future对象。如果需要使用回调函数处理异步计算的结果,可以考虑使用Guava库中的ListenableFuture类。

完整代码

 import com.alibaba.dubbo.common.threadpool.support.fixed.FixedThreadPool;
 import com.google.common.util.concurrent.*;
 import io.netty.util.concurrent.DefaultThreadFactory;
 ​
 import java.util.concurrent.*;
 ​
 public class Test {
 ​
     public static void main(String[] args) throws ExecutionException, InterruptedException {
         TaskTest taskTest = new TaskTest();
         taskTest.username = "123";
         taskTest.password = "123";
 ​
 ​
 //        ListeningExecutorService pool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5));
 //
 //        ListenableFuture<String> submit = pool.submit(taskTest);
 //        String s = submit.get();
         TestFuture();
     }
 ​
     public static void TestThreadPool() throws ExecutionException, InterruptedException {
         TaskTest taskTest = new TaskTest();
         taskTest.username = "123";
         taskTest.password = "123";
         ExecutorService threadPool = Executors.newScheduledThreadPool(5);
         Future<String> submit = threadPool.submit(taskTest);
         String s = submit.get();
         System.out.println(s);
         threadPool.shutdown();
     }
 ​
     public static void TestThreadPoolExecutor(){
         TaskTest taskTest = new TaskTest();
         taskTest.username = "123";
         taskTest.password = "123";
 ​
         ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
                 10,
                 60,
                 TimeUnit.SECONDS,
                 new ArrayBlockingQueue<Runnable>(20));
         ListeningExecutorService listeningPool = MoreExecutors.listeningDecorator(threadPoolExecutor);
         ListenableFuture<String> submit = listeningPool.submit(taskTest);
         //然而这个listen方法并不能有返回值
         submit.addListener(new Runnable() {
             @Override
             public void run() {
                 System.out.println("方法submit已执行完毕");
             }
         },threadPoolExecutor);
         //addlistener方法添加一个Runable的回调函数,当submit执行完毕之后,会调用第二个参数threadPoolExecutor的线程池执行这个回调函数
     }
 ​
     public static void TestFuture(){
         TaskTest taskTest = new TaskTest();
         taskTest.username = "123";
         taskTest.password = "123";
 ​
         ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
                 10,
                 60,
                 TimeUnit.SECONDS,
                 new ArrayBlockingQueue<Runnable>(20));
 ​
         ListeningExecutorService listeningPool = MoreExecutors.listeningDecorator(threadPoolExecutor);
         ListenableFuture<String> future = listeningPool.submit(taskTest);
         /*
           FutureCallBack接口可以对每个任务的成功或失败单独做出响应
          */
         FutureCallback<String> futureCallback = new FutureCallback<String>() {
             @Override
             public void onSuccess(String result) {
                 System.out.println("Futures.addCallback 能带返回值:" + result);
             }
             @Override
             public void onFailure(Throwable t) {
                 System.out.println("出错,业务回滚或补偿");
             }
         };
 ​
         //为任务绑定回调接口
         Futures.addCallback(future, futureCallback, listeningPool);
     }
 ​
 }
 ​
 ​
 class TaskTest implements Callable<String>{
     String username;
     String password;
 ​
     @Override
     public String call() throws Exception {
         return "task" + username + password;
     }
 }
 ​
 class TaskTest1 implements Runnable{
 ​
     @Override
     public void run() {
 ​
     }
 }
 ​
 class TaskTest2 extends Thread{
 ​
     @Override
     public void run() {
 ​
     }
 }
都看到这了点个赞帮过下活动呗