JDK1.8 创建线程池有哪几种方式?

简介: JDK1.8 创建线程池有哪几种方式?

JDK1.8 创建线程池有哪几种方式?

  • newFixedThreadPool


定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程


测试代码:

public class TestThreadPool {
 //定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程
 static ExecutorService fixedExecutor = Executors.newFixedThreadPool(3);
 public static void main(String[] args) {
  testFixedExecutor();
 }
 //测试定长线程池,线程池的容量为3,提交6个任务,根据打印结果可以看出先执行前3个任务,3个任务结束后再执行后面的任务
 private static void testFixedExecutor() {
  for (int i = 0; i < 6; i++) {
   final int index = i;
   fixedExecutor.execute(new Runnable() {
    public void run() {
     try {
      Thread.sleep(3000);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
     System.out.println(Thread.currentThread().getName() + " index:" + index);
    }
   });
  }
  try {
   Thread.sleep(4000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  System.out.println("4秒后...");
  fixedExecutor.shutdown();
 }
}

打印结果:

pool-1-thread-1 index:0
pool-1-thread-2 index:1
pool-1-thread-3 index:2
4秒后...
pool-1-thread-3 index:5
pool-1-thread-1 index:3
pool-1-thread-2 index:4
  • newCachedThreadPool


可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制


测试代码:

public class TestThreadPool {
 //可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制
 static ExecutorService cachedExecutor = Executors.newCachedThreadPool();
 public static void main(String[] args) {
  testCachedExecutor();
 }
 //测试可缓存线程池
 private static void testCachedExecutor() {
  for (int i = 0; i < 6; i++) {
   final int index = i;
   cachedExecutor.execute(new Runnable() {
    public void run() {
     try {
      Thread.sleep(3000);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
     System.out.println(Thread.currentThread().getName() + " index:" + index);
    }
   });
  }
  try {
   Thread.sleep(4000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  System.out.println("4秒后...");
  cachedExecutor.shutdown();
 }
}


打印结果:

pool-1-thread-1 index:0
pool-1-thread-6 index:5
pool-1-thread-5 index:4
pool-1-thread-4 index:3
pool-1-thread-3 index:2
pool-1-thread-2 index:1
4秒后...
  • newScheduledThreadPool 定长线程池,可执行周期性的任务

测试代码:

public class TestThreadPool {
 //定长线程池,可执行周期性的任务
 static ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(3);
 public static void main(String[] args) {
  testScheduledExecutor();
 }
 //测试定长、可周期执行的线程池
 private static void testScheduledExecutor() {
  for (int i = 0; i < 3; i++) {
   final int index = i;
   //scheduleWithFixedDelay 固定的延迟时间执行任务;scheduleAtFixedRate 固定的频率执行任务
   scheduledExecutor.scheduleWithFixedDelay(new Runnable() {
    public void run() {
     System.out.println(Thread.currentThread().getName() + " index:" + index);
    }
   }, 0, 3, TimeUnit.SECONDS);
  }
  try {
   Thread.sleep(4000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  System.out.println("4秒后...");
  scheduledExecutor.shutdown();
 }
}

打印结果:

pool-1-thread-1 index:0
pool-1-thread-2 index:1
pool-1-thread-3 index:2
pool-1-thread-1 index:0
pool-1-thread-3 index:1
pool-1-thread-1 index:2
4秒后...


  • newSingleThreadExecutor

单线程的线程池,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行

测试代码:

public class TestThreadPool {
 //单线程的线程池,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行
 static ExecutorService singleExecutor = Executors.newSingleThreadExecutor();
 public static void main(String[] args) {
  testSingleExecutor();
 }
 //测试单线程的线程池
 private static void testSingleExecutor() {
  for (int i = 0; i < 3; i++) {
   final int index = i;
   singleExecutor.execute(new Runnable() {
    public void run() {
     try {
      Thread.sleep(3000);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
     System.out.println(Thread.currentThread().getName() + " index:" + index);
    }
   });
  }
  try {
   Thread.sleep(4000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  System.out.println("4秒后...");
  singleExecutor.shutdown();
 }
}


打印结果:

pool-1-thread-1 index:0
4秒后...
pool-1-thread-1 index:1
pool-1-thread-1 index:2
  • newSingleThreadScheduledExecutor

单线程可执行周期性任务的线程池

测试代码:

public class TestThreadPool {
 //单线程可执行周期性任务的线程池
 static ScheduledExecutorService singleScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
 public static void main(String[] args) {
  testSingleScheduledExecutor();
 }
 //测试单线程可周期执行的线程池
 private static void testSingleScheduledExecutor() {
  for (int i = 0; i < 3; i++) {
   final int index = i;
   //scheduleWithFixedDelay 固定的延迟时间执行任务;scheduleAtFixedRate 固定的频率执行任务
   singleScheduledExecutor.scheduleAtFixedRate(new Runnable() {
    public void run() {
     System.out.println(Thread.currentThread().getName() + " index:" + index);
    }
   }, 0, 3, TimeUnit.SECONDS);
  }
  try {
   Thread.sleep(4000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  System.out.println("4秒后...");
  singleScheduledExecutor.shutdown();
 }
}

打印结果

pool-1-thread-1 index:0
pool-1-thread-1 index:1
pool-1-thread-1 index:2
pool-1-thread-1 index:0
pool-1-thread-1 index:1
pool-1-thread-1 index:2
4秒后...


newWorkStealingPool

任务窃取线程池,不保证执行顺序,适合任务耗时差异较大。


线程池中有多个线程队列,有的线程队列中有大量的比较耗时的任务堆积,而有的线程队列却是空的,就存在有的线程处于饥饿状态,当一个线程处于饥饿状态时,它就会去其它的线程队列中窃取任务。解决饥饿导致的效率问题。


默认创建的并行 level 是 CPU 的核数。主线程结束,即使线程池有任务也会立即停止。


测试代码:

public class TestThreadPool {
 //任务窃取线程池
 static ExecutorService workStealingExecutor = Executors.newWorkStealingPool();
 public static void main(String[] args) {
  testWorkStealingExecutor();
 }
 //测试任务窃取线程池
 private static void testWorkStealingExecutor() {
  for (int i = 0; i < 10; i++) {//本机 CPU 8核,这里创建10个任务进行测试
   final int index = i;
   workStealingExecutor.execute(new Runnable() {
    public void run() {
     try {
      Thread.sleep(3000);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
     System.out.println(Thread.currentThread().getName() + " index:" + index);
    }
   });
  }
  try {
   Thread.sleep(4000);//这里主线程不休眠,不会有打印输出
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  System.out.println("4秒后...");
//  workStealingExecutor.shutdown();
 }
}

打印结果如下,index:8,index:9并未打印出:

ForkJoinPool-1-worker-1 index:0
ForkJoinPool-1-worker-7 index:6
ForkJoinPool-1-worker-5 index:4
ForkJoinPool-1-worker-3 index:2
ForkJoinPool-1-worker-4 index:3
ForkJoinPool-1-worker-2 index:1
ForkJoinPool-1-worker-0 index:7
ForkJoinPool-1-worker-6 index:5
4秒后..


Executors创建线程池有哪几种方式?

Executors如何创建线程池?


Executors 类是从 JDK 1.5 开始就新增的线程池创建的静态工厂类,它就是创建线程池的,但是很多的大厂已经不建议使用该类去创建线程池。原因在于,该类创建的很多线程池的内部使用了无界任务队列,在并发量很大的情况下会导致 JVM 抛出 OutOfMemoryError,直接让 JVM 崩溃,影响严重。


但是 Executors 类究竟是如何使用的?


\1. newFixedThreadPool,创建定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程。


package constxiong.concurrency.a011;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 测试创建定长线程池
 * @author ConstXiong
 */
public class TestNewFixedThreadPool {
  public static void main(String[] args) {
    //创建工作线程数为 3 的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
    //提交 6 个任务
    for (int i = 0; i <6; i++) {
      final int index = i;
      fixedThreadPool.execute(() -> {
        try {
          //休眠 3 秒
          Thread.sleep(3000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " index:" + index);
      });
    }
    try {
      Thread.sleep(4000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("4秒后...");
    //关闭线程池后,已提交的任务仍然会执行完
    fixedThreadPool.shutdown();
  }
}


打印结果:

pool-1-thread-2 index:1
pool-1-thread-3 index:2
pool-1-thread-1 index:0
4秒后...
pool-1-thread-1 index:4
pool-1-thread-3 index:5
pool-1-thread-2 index:3

\2. newCachedThreadPool,创建可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制。

package constxiong.concurrency.a011;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 测试创建可缓存的线程池
 * @author ConstXiong
 */
public class TestNewCachedThreadPool {
  public static void main(String[] args) {
    //创建可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制
    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    for (int i = 0; i <6; i++) {
      final int index = i;
      cachedThreadPool.execute(() -> {
        try {
          Thread.sleep(3000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " index:" + index);
      });
    }
    try {
      Thread.sleep(4000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("4秒后...");
    cachedThreadPool.shutdown();
  }
}


打印结果可以看出,创建的线程数与任务数相等

pool-1-thread-1 index:0
pool-1-thread-3 index:2
pool-1-thread-6 index:5
pool-1-thread-4 index:3
pool-1-thread-5 index:4
pool-1-thread-2 index:1
4秒后...


\3. newScheduledThreadPool,创建定长线程池,可执行周期性的任务。

package constxiong.concurrency.a011;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
 * 测试创建定长线程池,可执行周期性的任务
 * @author ConstXiong
 */
public class TestNewScheduledThreadPool {
  public static void main(String[] args) {
    //创建定长线程池,可执行周期性的任务
    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
    for (int i = 0; i <3; i++) {
      final int index = i;
      //scheduleWithFixedDelay 固定的延迟时间执行任务; scheduleAtFixedRate 固定的频率执行任务
      scheduledThreadPool.scheduleWithFixedDelay(() -> {
          System.out.println(Thread.currentThread().getName() + " index:" + index);
      }, 0, 3, TimeUnit.SECONDS);
    }
    try {
      Thread.sleep(4000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("4秒后...");
    scheduledThreadPool.shutdown();
  }
}


打印结果:

pool-1-thread-1 index:0
pool-1-thread-3 index:2
pool-1-thread-2 index:1
pool-1-thread-1 index:0
pool-1-thread-2 index:1
pool-1-thread-3 index:2
4秒后...

\4. newSingleThreadExecutor,创建单线程的线程池,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行。

package constxiong.concurrency.a011;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 测试单线程的线程池
 * @author ConstXiong
 */
public class TestNewSingleThreadExecutor {
  public static void main(String[] args) {
    //单线程的线程池,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行
    ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
    //提交 3 个任务
    for (int i = 0; i <3; i++) {
      final int index = i;
      singleThreadPool.execute(() -> {
        //执行第二个任务时,报错,测试线程池会创建新的线程执行任务三
        if (index == 1) {
          throw new RuntimeException("线程执行出现异常");
        }
        try {
          Thread.sleep(3000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " index:" + index);
      });
    }
    try {
      Thread.sleep(4000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("4秒后...");
    singleThreadPool.shutdown();
  }
}


打印结果可以看出,即使任务出现了异常,线程池还是会自动补充一个线程继续执行下面的任务

pool-1-thread-1 index:0
Exception in thread "pool-1-thread-1" 
java.lang.RuntimeException: 线程执行出现异常
  at constxiong.concurrency.a011.TestNewSingleThreadExecutor.lambda$0(TestNewSingleThreadExecutor.java:21)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  at java.lang.Thread.run(Thread.java:748)
4秒后...
pool-1-thread-2 index:2


\5. newSingleThreadScheduledExecutor,创建单线程可执行周期性任务的线程池。

package constxiong.concurrency.a011;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
 * 测试单线程可执行周期性任务的线程池
 * @author ConstXiong
 */
public class TestNewSingleThreadScheduledExecutor {
  public static void main(String[] args) {
    //创建单线程可执行周期性任务的线程池
    ScheduledExecutorService singleScheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
    //提交 3 个固定频率执行的任务
    for (int i = 0; i <3; i++) {
      final int index = i;
      //scheduleWithFixedDelay 固定的延迟时间执行任务; scheduleAtFixedRate 固定的频率执行任务
      singleScheduledThreadPool.scheduleAtFixedRate(() -> {
        System.out.println(Thread.currentThread().getName() + " index:" + index);
      }, 0, 3, TimeUnit.SECONDS);
    }
    try {
      Thread.sleep(4000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("4秒后...");
    singleScheduledThreadPool.shutdown();
  }
}


打印机结果可以看出 0-2 任务都被执行了 2 个周期


pool-1-thread-1 index:0
pool-1-thread-1 index:1
pool-1-thread-1 index:2
pool-1-thread-1 index:0
pool-1-thread-1 index:1
pool-1-thread-1 index:2
4秒后...


\6. newWorkStealingPool,创建任务可窃取线程池,空闲线程可以窃取其他任务队列的任务,不保证执行顺序,适合任务耗时差异较大。

package constxiong.concurrency.a011;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 测试可任务窃取线程池
 * @author ConstXiong
 */
public class TestNewWorkStealingPool {
  public static void main(String[] args) {
    //创建 4个工作线程的 任务可窃取线程池,如果不设置并行数,默认取 CPU 总核数
    ExecutorService workStealingThreadPool = Executors.newWorkStealingPool(4);
    for (int i = 0; i <10; i++) {
      final int index = i;
      workStealingThreadPool.execute(() -> {
        try {
          //模拟任务执行时间为 任务编号为0 1 2 的执行时间需要 3秒;其余任务200 毫秒,导致任务时间差异较大
          if (index <= 2) {
            Thread.sleep(3000);
          } else {
            Thread.sleep(200);
          }
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " index:" + index);
      });
    }
    try {
      Thread.sleep(10000);//休眠 10 秒
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("10秒后...");
  }
}


打印结果可以看出,线程 ForkJoinPool-1-worker-0 把3-9的任务都执行完

ForkJoinPool-1-worker-0 index:3
ForkJoinPool-1-worker-0 index:4
ForkJoinPool-1-worker-0 index:5
ForkJoinPool-1-worker-0 index:6
ForkJoinPool-1-worker-0 index:7
ForkJoinPool-1-worker-0 index:8
ForkJoinPool-1-worker-0 index:9
ForkJoinPool-1-worker-1 index:0
ForkJoinPool-1-worker-3 index:2
ForkJoinPool-1-worker-2 index:1
10秒后...
目录
相关文章
|
6月前
|
Java 调度 开发者
JDK 21中的虚拟线程:轻量级并发的新篇章
本文深入探讨了JDK 21中引入的虚拟线程(Virtual Threads)概念,分析了其背后的设计哲学,以及与传统线程模型的区别。文章还将讨论虚拟线程如何简化并发编程,提高资源利用率,并展示了一些使用虚拟线程进行开发的示例。
1069 4
|
6月前
|
缓存 安全 Java
JDK8线程池BUG引发的思考
JDK8线程池BUG引发的思考
149 0
|
6月前
|
存储 缓存 并行计算
【面试问题】JDK并发类库提供的线程池实现有哪些?
【1月更文挑战第27天】【面试问题】JDK并发类库提供的线程池实现有哪些?
|
12月前
|
算法 安全 Java
【Java】JDK 21中的虚拟线程以及其他新特性
【Java】JDK 21中的虚拟线程以及其他新特性
211 0
|
1月前
|
Dubbo Java 应用服务中间件
剖析Tomcat线程池与JDK线程池的区别和联系!
剖析Tomcat线程池与JDK线程池的区别和联系!
剖析Tomcat线程池与JDK线程池的区别和联系!
|
21天前
|
监控 数据可视化 Java
如何使用JDK自带的监控工具JConsole来监控线程池的内存使用情况?
如何使用JDK自带的监控工具JConsole来监控线程池的内存使用情况?
|
2月前
|
监控 数据可视化 Java
使用JDK自带的监控工具JConsole来监控线程池的内存使用情况
使用JDK自带的监控工具JConsole来监控线程池的内存使用情况
|
3月前
|
缓存 Java 调度
【Java 并发秘籍】线程池大作战:揭秘 JDK 中的线程池家族!
【8月更文挑战第24天】Java的并发库提供多种线程池以应对不同的多线程编程需求。本文通过实例介绍了四种主要线程池:固定大小线程池、可缓存线程池、单一线程线程池及定时任务线程池。固定大小线程池通过预设线程数管理任务队列;可缓存线程池能根据需要动态调整线程数量;单一线程线程池确保任务顺序执行;定时任务线程池支持周期性或延时任务调度。了解并正确选用这些线程池有助于提高程序效率和资源利用率。
52 2
|
3月前
|
算法 Java
JDK版本特性问题之想控制 G1 垃圾回收器的并行工作线程数量,如何解决
JDK版本特性问题之想控制 G1 垃圾回收器的并行工作线程数量,如何解决
|
5月前
|
存储 网络协议 Java
【JDK21】详解虚拟线程
【JDK21】详解虚拟线程
183 0