多线程&&线程池

迷南。 2022-12-29 00:26 359阅读 0赞

多线程

多线程的实现方式主要有三种

  • extends Thread
  • implements Runnable
  • implements Callable

实现接口 VS 继承 Thread

实现接口会更好一些,因为:

  • Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口;
  • 类可能只要求可执行就行,继承整个 Thread 类开销过大。
  • extends Thread

继承 Thread 的代码

  1. public class ExtendsThread extends Thread {
  2. /**
  3. * 线程名称
  4. */
  5. private String threadName;
  6. /**
  7. * 构造函数
  8. *
  9. * @param threadName 线程名称
  10. */
  11. public ExtendsThread(String threadName) {
  12. this.threadName = threadName;
  13. }
  14. /**
  15. * 重写run方式
  16. */
  17. @Override
  18. public void run() {
  19. System.out.println(threadName);
  20. }
  21. }
  22. public class ThreadMain {
  23. public static void main(String[] args) {
  24. ExtendsThread extendsThread = new ExtendsThread("1");
  25. extendsThread.start();
  26. extendsThread = new ExtendsThread("2");
  27. extendsThread.start();
  28. }
  29. }
  • implements Runnable

实现Runnable接口

  1. public class ThreadMain {
  2. public static void main(String[] args) {
  3. ThreadImplRunnable threadImplRunnable = new ThreadImplRunnable("Thread1");
  4. new Thread(threadImplRunnable).start();
  5. threadImplRunnable = new ThreadImplRunnable("Thread2");
  6. new Thread(threadImplRunnable).start();
  7. System.out.println("end");
  8. }
  9. }
  10. public class ThreadImplRunnable implements Runnable {
  11. /**
  12. * 线程名称
  13. */
  14. private String threadName;
  15. /**
  16. * 构造函数
  17. *
  18. * @param threadName 线程名称
  19. */
  20. public ThreadImplRunnable(String threadName) {
  21. this.threadName = threadName;
  22. }
  23. /**
  24. * 重写run方法
  25. */
  26. @Override
  27. public void run() {
  28. System.out.println(threadName);
  29. }
  30. }
  31. end
  32. Thread1
  33. Thread2
  • implements Callable

实现Callable接口主要是可以获取到返回值,futureTask.get() 获取返回的值

  1. public class ThreadImplCallable<T> implements Callable<T> {
  2. /**
  3. * 线程名称
  4. */
  5. private String threadName;
  6. /**
  7. * 构造函数
  8. *
  9. * @param threadName 线程名称
  10. */
  11. public ThreadImplCallable(String threadName) {
  12. this.threadName = threadName;
  13. }
  14. /**
  15. * 重写call方法
  16. */
  17. @Override
  18. public T call() throws Exception {
  19. System.out.println(threadName);
  20. return (T)threadName;
  21. }
  22. }
  23. public class ThreadMain {
  24. public static void main(String[] args) throws ExecutionException, InterruptedException {
  25. Callable<String> threadImplCallable = new ThreadImplCallable<>("Thread1");
  26. FutureTask<String> futureTask = new FutureTask<>(threadImplCallable);
  27. Thread thread = new Thread(futureTask);
  28. thread.start();
  29. String rValue = futureTask.get();
  30. System.out.println("Thread1 return value is " + rValue);
  31. threadImplCallable = new ThreadImplCallable<>("Thread2");
  32. futureTask = new FutureTask<>(threadImplCallable);
  33. thread = new Thread(futureTask);
  34. thread.start();
  35. rValue = futureTask.get();
  36. System.out.println("Thread2 return value is " + rValue);
  37. System.out.println("end");
  38. }
  39. }
  40. Thread1
  41. Thread1 return value is Thread1
  42. Thread2
  43. Thread2 return value is Thread2
  44. end

线程池

java 线程池的顶级接口是 Executor, 只定义了一个方法 execute,ExecutorService继承接口Executor接口,并定义了其他的相关的接口,ExecutorService下面有一个继承的抽象类AbstractExecutorService和实现了定时调度的接口ScheduledExecutorService,而又有两个类继承抽象类,分别是ThreadPoolExecutor和ForkJoinPool,而ScheduledThreadPoolExecutor则实现接口实现ScheduledExecutorService接口和继承ThreadPoolExecutor类,基本的关系就是这样的,而且基本常用的封装方法都在Executors类中,只要 Executors.xxxx 就可以应用对应的封装的方法。

Executor

Executor 管理多个异步任务的执行,而无需程序员显式地管理线程的生命周期。这里的异步是指多个任务的执行互不干扰,不需要进行同步操作。

线程池种类

  • newSingleThreadExecutor

一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

  1. import java.util.concurrent.ExecutorService;
  2. import java.util.concurrent.Executors;
  3. public class ThreadPool {
  4. public static void main(String[] args) {
  5. ExecutorService pool = Executors.newSingleThreadExecutor();
  6. for (int i = 0; i < 10; i++) {
  7. pool.execute(() -> {
  8. System.out.println(Thread.currentThread().getName() + "\t开始发车啦....");
  9. });
  10. }
  11. }
  12. }
  13. pool-1-thread-1 开始发车啦....
  14. pool-1-thread-1 开始发车啦....
  15. pool-1-thread-1 开始发车啦....
  16. pool-1-thread-1 开始发车啦....
  17. pool-1-thread-1 开始发车啦....
  18. pool-1-thread-1 开始发车啦....
  19. pool-1-thread-1 开始发车啦....
  20. pool-1-thread-1 开始发车啦....
  21. pool-1-thread-1 开始发车啦....
  22. pool-1-thread-1 开始发车啦....
  • newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

  1. import java.util.concurrent.ExecutorService;
  2. import java.util.concurrent.Executors;
  3. public class ThreadPool {
  4. public static void main(String[] args) {
  5. ExecutorService pool = Executors.newFixedThreadPool(10);
  6. for (int i = 0; i < 10; i++) {
  7. pool.execute(() -> {
  8. System.out.println(Thread.currentThread().getName() + "\t开始发车啦....");
  9. });
  10. }
  11. }
  12. }
  13. pool-1-thread-1 开始发车啦....
  14. pool-1-thread-4 开始发车啦....
  15. pool-1-thread-3 开始发车啦....
  16. pool-1-thread-2 开始发车啦....
  17. pool-1-thread-6 开始发车啦....
  18. pool-1-thread-7 开始发车啦....
  19. pool-1-thread-5 开始发车啦....
  20. pool-1-thread-8 开始发车啦....
  21. pool-1-thread-9 开始发车啦....
  22. pool-1-thread-10 开始发车啦....
  • newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲的线程,当任务数增加时,此线程池又添加新线程来处理任务。

  1. public static void main(String[] args) {
  2. ExecutorService pool = Executors.newCachedThreadPool();
  3. for (int i = 0; i < 10; i++) {
  4. pool.execute(() -> {
  5. System.out.println(Thread.currentThread().getName() + "\t开始发车啦....");
  6. });
  7. }
  8. }
  9. pool-1-thread-1 开始发车啦....
  10. pool-1-thread-2 开始发车啦....
  11. pool-1-thread-2 开始发车啦....
  12. pool-1-thread-3 开始发车啦....
  13. pool-1-thread-3 开始发车啦....
  14. pool-1-thread-1 开始发车啦....
  15. pool-1-thread-7 开始发车啦....
  16. pool-1-thread-5 开始发车啦....
  17. pool-1-thread-6 开始发车啦....
  18. pool-1-thread-4 开始发车啦....
  • newScheduledThreadPool

此线程池支持定时以及周期性执行任务的需求。

  1. import java.util.concurrent.Executors;
  2. import java.util.concurrent.ScheduledExecutorService;
  3. import java.util.concurrent.TimeUnit;
  4. public class ThreadPool {
  5. public static void main(String[] args) {
  6. ScheduledExecutorService pool = Executors.newScheduledThreadPool(10);
  7. for (int i = 0; i < 10; i++) {
  8. pool.schedule(() -> {
  9. System.out.println(Thread.currentThread().getName() + "\t开始发车啦....");
  10. }, 10, TimeUnit.SECONDS);
  11. }
  12. }
  13. }
  14. pool-1-thread-1 开始发车啦....
  15. pool-1-thread-2 开始发车啦....
  16. pool-1-thread-1 开始发车啦....
  17. pool-1-thread-3 开始发车啦....
  18. pool-1-thread-7 开始发车啦....
  19. pool-1-thread-8 开始发车啦....
  20. pool-1-thread-6 开始发车啦....
  21. pool-1-thread-5 开始发车啦....
  22. pool-1-thread-2 开始发车啦....
  23. pool-1-thread-4 开始发车啦....

上面演示的是延迟10秒执行任务,如果想要执行周期性的任务可以用下面的方式,每秒执行一次。

  1. public static void main(String[] args) {
  2. ScheduledExecutorService pool = Executors.newScheduledThreadPool(10);
  3. //pool.scheduleWithFixedDelay也可以
  4. pool.scheduleAtFixedRate(() -> {
  5. System.out.println(Thread.currentThread().getName() + "\t开始发车啦....");
  6. }, 1, 1, TimeUnit.SECONDS);
  7. }
  8. pool-1-thread-1 开始发车啦....
  9. pool-1-thread-1 开始发车啦....
  10. pool-1-thread-2 开始发车啦....
  11. pool-1-thread-1 开始发车啦....
  12. pool-1-thread-3 开始发车啦....
  13. pool-1-thread-2 开始发车啦....
  14. pool-1-thread-4 开始发车啦....
  15. pool-1-thread-1 开始发车啦....
  16. pool-1-thread-5 开始发车啦....
  17. pool-1-thread-3 开始发车啦....
  18. pool-1-thread-3 开始发车啦....
  19. pool-1-thread-3 开始发车啦....
  20. pool-1-thread-3 开始发车啦....
  21. pool-1-thread-3 开始发车啦....
  22. pool-1-thread-3 开始发车啦....
  23. pool-1-thread-3 开始发车啦....
  24. pool-1-thread-9 开始发车啦....
  25. pool-1-thread-9 开始发车啦....
  • newWorkStealingPool

newWorkStealingPool是jdk1.8才有的,会根据所需的并行层次来动态创建和关闭线程,通过使用多个队列减少竞争,底层用的ForkJoinPool来实现的。ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。

newWorkStealingPool适合使用在很耗时的操作,但是newWorkStealingPool不是ThreadPoolExecutor的扩展,它是新的线程池类ForkJoinPool的扩展,但是都是在统一的一个Executors类中实现,由于能够合理的使用CPU进行对任务操作(并行操作),所以适合使用在很耗时的任务中

  1. package org.jeecg.modules.exam.entity;
  2. import org.junit.Test;
  3. import java.util.concurrent.*;
  4. public class WorkStealingPoolTest {
  5. // 线程数
  6. private static final int threads = 10;
  7. // 用于计数线程是否执行完成
  8. CountDownLatch countDownLatch = new CountDownLatch(threads);
  9. /**
  10. * newFixedThreadPool execute
  11. *
  12. * @throws ExecutionException
  13. * @throws InterruptedException
  14. */
  15. @Test
  16. public void test1() throws ExecutionException, InterruptedException {
  17. System.out.println("---- start ----");
  18. ExecutorService executorService = Executors.newWorkStealingPool();
  19. for (int i = 0; i < threads; i++) {
  20. executorService.execute(() -> {
  21. try {
  22. System.out.println(Thread.currentThread().getName());
  23. } catch (Exception e) {
  24. System.out.println(e);
  25. } finally {
  26. countDownLatch.countDown();
  27. }
  28. });
  29. }
  30. countDownLatch.await();
  31. System.out.println("---- end ----");
  32. }
  33. /**
  34. * newFixedThreadPool submit submit
  35. */
  36. @Test
  37. public void test2() throws InterruptedException {
  38. System.out.println("---- start ----");
  39. ExecutorService executorService = Executors.newWorkStealingPool();
  40. for (int i = 0; i < threads; i++) {
  41. // Callable 带返回值
  42. executorService.submit(new Thread(new Runnable() {
  43. @Override
  44. public void run() {
  45. try {
  46. System.out.println(Thread.currentThread().getName());
  47. } catch (Exception e) {
  48. e.printStackTrace();
  49. } finally {
  50. countDownLatch.countDown();
  51. }
  52. }
  53. }));
  54. }
  55. countDownLatch.await();
  56. System.out.println("---- end ----");
  57. }
  58. /**
  59. * newFixedThreadPool submit Callable
  60. *
  61. * @throws ExecutionException
  62. * @throws InterruptedException
  63. */
  64. @Test
  65. public void test3() throws ExecutionException, InterruptedException {
  66. System.out.println("---- start ----");
  67. ExecutorService executorService = Executors.newWorkStealingPool();
  68. for (int i = 0; i < threads; i++) {
  69. // Runnable 带返回值
  70. FutureTask<?> futureTask = new FutureTask<>(new Callable<String>() {
  71. /**
  72. * call
  73. * @return currentThreadName
  74. */
  75. @Override
  76. public String call() {
  77. return Thread.currentThread().getName();
  78. }
  79. });
  80. executorService.submit(new Thread(futureTask));
  81. System.out.println(futureTask.get());
  82. }
  83. System.out.println("---- end ----");
  84. }
  85. }

发表评论

表情:
评论列表 (有 0 条评论,359人围观)

还没有评论,来说两句吧...

相关阅读

    相关 java线-线

    线程池-基本原理 概述 : ​ 提到池,大家应该能想到的就是水池。水池就是一个容器,在该容器中存储了很多的水。那么什么是线程池呢?线程池也是可以看做成一个池子,在该池子

    相关 线线

    前言: 1. 系统启动一个新线程的成本是比较高的,因为它涉及与操作系统交互。在这种情形下,使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,