你真的了解ForkJoinPool吗?这些技巧让你的代码性能提升十倍!

ForkJoinPool是Java 7中新增的一种线程池实现,它主要用于执行大量的计算密集型任务。F
首页 新闻资讯 行业资讯 你真的了解ForkJoinPool吗?这些技巧让你的代码性能提升十倍!

24e59b0572f80ad9d8f7226d4cce60d8d93f2c.png

1、线程池简介

线程池是一种常见的多线程编程方式,它可以有效地管理线程的创建、销毁和复用,从而提高程序的性能和稳定性。Java中提供了多种线程池实现,包括ForkJoinPool、Executors、CompletionService等。

2、ForkJoinPool

ForkJoinPool是Java 7中新增的一种线程池实现,它主要用于执行大量的计算密集型任务。ForkJoinPool采用“工作窃取”算法,即当某个线程的任务执行完毕后,会从其他线程的任务队列中窃取任务执行,从而实现负载均衡。

以下是一个使用ForkJoinPool的示例代码:

importjava.util.concurrent.*;publicclassForkJoinPoolExample{publicstaticvoidmain(String[]args){intn=1000000;int[]array=newint[n];for(inti=0;i<n;i++){array[i]=i;}ForkJoinPoolpool=newForkJoinPool();intsum=pool.invoke(newSumTask(array,0,n));System.out.println("Sum: "+sum);}}classSumTaskextendsRecursiveTask<Integer>{privateint[]array;privateintstart;privateintend;publicSumTask(int[]array,intstart,intend){this.array=array;this.start=start;this.end=end;}@OverrideprotectedIntegercompute(){if(end-start<=1000){intsum=0;for(inti=start;i<end;i++){sum+=array[i];}returnsum;}else{intmid=(start+end)/2;SumTaskleft=newSumTask(array,start,mid);SumTaskright=newSumTask(array,mid,end);left.fork();right.fork();returnleft.join()+right.join();}}}

以上代码创建了一个ForkJoinPool,用于计算一个长度为1000000的数组的元素之和。其中,SumTask是一个继承自RecursiveTask的任务类,用于将数组分成若干个子任务进行计算。当子任务的数量小于等于1000时,直接计算子任务的结果;否则,将子任务分成两个部分,分别交给左右两个子任务进行计算,最后将两个子任务的结果相加。

3、Executors

Executors是Java中提供的一个线程池工具类,它可以方便地创建各种类型的线程池。Executors提供了多个静态方法,用于创建不同类型的线程池,例如newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor等。

(1)newFixedThreadPool

newFixedThreadPool是一个固定大小的线程池,它会一直保持固定数量的线程,如果有新的任务提交,但线程池中的线程都在忙碌,那么新的任务就会进入等待队列中等待执行。

以下是一个使用newFixedThreadPool创建线程池的示例代码:

importjava.util.concurrent.*;publicclassFixedThreadPoolExample{publicstaticvoidmain(String[]args){ExecutorServiceexecutor=Executors.newFixedThreadPool(5);for(inti=0;i<20;i++){Runnabletask=newRunnable(){@Overridepublicvoidrun(){System.out.println("Task executed by "+Thread.currentThread().getName());}};executor.execute(task);}executor.shutdown();}}

以上代码创建了一个固定大小为5的线程池,提交了20个任务,并关闭了线程池。每个任务的执行会输出执行线程的名称。

(2)newCachedThreadPool

newCachedThreadPool是一个可缓存的线程池,它会根据需要创建新的线程,如果有线程空闲时间超过60秒,就会被回收。如果有新的任务提交,但线程池中的线程都在忙碌,那么就会创建新的线程来处理任务。

以下是一个使用newCachedThreadPool创建线程池的示例代码:

importjava.util.concurrent.*;publicclassCachedThreadPoolExample{publicstaticvoidmain(String[]args){ExecutorServiceexecutor=Executors.newCachedThreadPool();for(inti=0;i<20;i++){Runnabletask=newRunnable(){@Overridepublicvoidrun(){System.out.println("Task executed by "+Thread.currentThread().getName());}};executor.execute(task);}executor.shutdown();}}

以上代码创建了一个可缓存的线程池,提交了20个任务,并关闭了线程池。每个任务的执行会输出执行线程的名称。

(3)newSingleThreadExecutor

newSingleThreadExecutor是一个单线程的线程池,它会保证所有任务按照顺序执行,即每个任务都会在前一个任务执行完毕后才会执行。

以下是一个使用newSingleThreadExecutor创建线程池的示例代码:

importjava.util.concurrent.*;publicclassSingleThreadExecutorExample{publicstaticvoidmain(String[]args){ExecutorServiceexecutor=Executors.newSingleThreadExecutor();for(inti=0;i<20;i++){Runnabletask=newRunnable(){@Overridepublicvoidrun(){System.out.println("Task executed by "+Thread.currentThread().getName());}};executor.execute(task);}executor.shutdown();}}

以上代码创建了一个单线程的线程池,提交了20个任务,并关闭了线程池。每个任务的执行会输出执行线程的名称。

4、CompletionService

CompletionService是Java中提供的一个用于异步执行任务的工具类,它可以方便地获取已完成的任务的结果。CompletionService内部维护了一个阻塞队列,用于存储已完成的任务的结果。

以下是一个使用CompletionService的示例代码:

importjava.util.concurrent.*;publicclassCompletionServiceExample{publicstaticvoidmain(String[]args)throwsInterruptedException,ExecutionException{ExecutorServiceexecutor=Executors.newFixedThreadPool(5);CompletionService<Integer>completionService=newExecutorCompletionService<>(executor);for(inti=0;i<10;i++){finalintindex=i;Callable<Integer>task=newCallable<Integer>(){@OverridepublicIntegercall()throwsException{Thread.sleep((long)(Math.random()*1000));returnindex;}};completionService.submit(task);}for(inti=0;i<10;i++){Future<Integer>future=completionService.take();System.out.println("Result: "+future.get());}executor.shutdown();}}

以上代码创建了一个固定大小为5的线程池,提交了10个任务,并使用CompletionService获取已完成的任务的结果。每个任务会随机休眠一段时间,然后返回任务的编号。在主线程中,使用completionService.take()方法获取已完成的任务的结果,并输出任务的编号。

5、Callable和Future

Callable是Java中提供的一个接口,它类似于Runnable接口,但是可以返回执行结果。Future是Java中提供的一个接口,它可以用于获取异步执行任务的结果。

以下是一个使用Callable和Future的示例代码:

importjava.util.concurrent.*;publicclassCallableAndFutureExample{publicstaticvoidmain(String[]args)throwsInterruptedException,ExecutionException{ExecutorServiceexecutor=Executors.newSingleThreadExecutor();Callable<Integer>task=newCallable<Integer>(){@OverridepublicIntegercall()throwsException{Thread.sleep(1000);return1+2;}};Future<Integer>future=executor.submit(task);System.out.println("Result: "+future.get());executor.shutdown();}}

以上代码创建了一个单线程的线程池,提交了一个任务,并使用Future获取任务的执行结果。任务会休眠1秒钟,然后返回1+2的结果。在主线程中,使用future.get()方法获取任务的执行结果,并输出结果。

6、Runnable和Thread

Runnable是Java中提供的一个接口,它表示一个可以被线程执行的任务。Thread是Java中提供的一个类,它表示一个线程。

以下是一个使用Runnable和Thread的示例代码:

publicclassRunnableAndThreadExample{publicstaticvoidmain(String[]args)throwsInterruptedException{Runnabletask=newRunnable(){@Overridepublicvoidrun(){System.out.println("Task executed by "+Thread.currentThread().getName());}};Threadthread=newThread(task);thread.start();thread.join();}}

以上代码创建了一个任务,并使用Thread将任务提交到一个新的线程中执行。在主线程中,使用thread.join()方法等待新线程执行完毕。每个任务的执行会输出执行线程的名称。

7、总结

本文介绍了Java中常见的线程池实现,包括ForkJoinPool、Executors、CompletionService、Callable、Future、Runnable等知识点的详细讲解和完整可运行的代码示例。线程池是Java中常见的多线程编程方式,它可以有效地管理线程的创建、销毁和复用,从而提高程序的性能和稳定性。在实际开发中,需要根据具体的需求选择合适的线程池实现。

4    2023-06-13 13:52:00    Java 7 线程池