zoukankan      html  css  js  c++  java
  • Java线程的学习_线程池

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

    线程池在系统启动时即创建大量空闲的线程,程序将一个Runnable对象或Callable对象传给线程池,线程池就会启动一个线程来run()或call()方法,当run()或call()方法执行结束后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个Runnable对象的run()或call()方法。除此之外,使用线程池可以有效地控制系统中并发线程的数量。

    使用Executors类来创建线程池:

    方法名
    newCachedThreadPool() 创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将会被缓存在线程池中
    newFixedThreadPool(int nThreads) 创建一个可重用的、具有固定线程数的线程池
    newSingleThreadExecutor() 创建一个只有单线程的线程池,它相当与调用newFixedThreadPool()方法时传入参数为1
    newScheduledThreadPool(int corePoolSize) 创建具有指定线程数的线程池,它可以在指定延迟后执行线程任务。corePoolSize指池中所保存的线程数,即使线程是空闲的也被保存在线程池内。
    newSingleThreadScheduledExecutor() 创建只有一个线程的线程池,它可以在指定延迟后执行线程任务
    ExecutorService newWorkStealingPool(int parallelism) 创建持有足够的线程的线程池来支持给定的并行级别,该方法还会使用多个队列来减少竞争
    ExecutorService newWorkStealingPool() 该方法是前一个方法的 简化版本,如果当前机器有4个CPU,则目标并行级别被设置为4,也就是相当于为前一个方法传入4作为参数。

    使用线程池来执行线程任务的步骤:
    1.调用Executors类的静态工厂方法创建一个ExecutorService对象,该对象代表一个线程池。
    2.创建Runnable实现类或Callable实现类的实例,作为线程执行任务。
    3.调用ExecutorService对象的submit()方法来提交Runnable实例或Callable实例。
    4.当不想提交任何任务时,调用ExecutorService对象的shutdown()方法来关闭线程池。

    使用线程池来执行指定Runnable对象所代表的的任务:

    public class ThreadPoolTest {

    public static void main(String[] args)throws Exception{
    //创建一个具有固定线程数(6)的线程池
    ExecutorService pool = Executors.newFixedThreadPool(6);
    //使用Lambda表达式创建Runnable对象 Runnable target = () -> {
    for(int i = 0; i < 100; i++){
    System.out.println(Thread.currentThread().getName() + "的i值为:" + i);
    }
    };
    //向线程池中提交两个线程
    pool.submit(target);
    pool.submit(target); //关闭线程池
    pool.shutdown();
    }
    }

    ForkJoinPool类

    ForkJoinPool类的作用是将一个任务拆分为多个“小任务”并行计算,再把多个“小任务”的结果合并成总的计算结果。ForkJoinPool类是ExecutorService的实现类,因此是一种特殊的线程池。

    方法
    ForkJoinPool(int parallelism) 创建一个包含parallelism个并行线程的ForkJoinPool
    ForkJoinPool() 以runtime.availableprocessors(处理器数量)返回值为parallelism参数来创建ForkJoinPool。
    ForkJoinPool commonPool() 该方法返回一个通用池,通用池的运行状态不回受shutdown()或shutdownNow()方法的影响。
    int getCommonPoolParallelism() 返回通用池的并行级别

    创建了ForkJoinPool实例之后,就可调用ForkJoinPool的submit(ForkJoinTask task)或invoke(ForkJoinTask task)方法来执行指定任务。其中ForkJoinTask代表一个可以并行、合并的任务。ForkJoinTask是一个抽象类,它还有两个抽象子类:RecursiveAction(没有返回值)和RecursiveTask(有返回值)。

    没有返回值的“大任务”拆分:

    //继承RecursiveAction来实现“可分解”的任务
    class PrintTask extends RecursiveAction{
    //每个“小任务”最多打印50个数
    private static final int
    THRESHOLD = 50;
    private int start;
    private int end;
    //打印从start到end的任务
    public PrintTask(int start, int end){
    this.start = start;
    this.end = end;
    }
    @Override
    protected void compute() {
    // 当end与start之间的差小于THRESHOLD时,开始打印
    if(end - start < THRESHOLD){
    for(int i = 0; i < end; i++){
    System.out.println(Thread.currentThread().getName() + "的i值:" + i);
    }
    }else{
    //当end与start之间的差大于THRESHOLD时,开始打印
    //将大任务分解成两个“小任务”
    int middle = (start + end) / 2;
    PrintTask left = new PrintTask(start, middle);
    PrintTask right = new PrintTask(middle, end);
    //并执行两个“小任务”
    left.fork();
    right.fork();
    }

    }
    }
    public class ForkJoinPoolTest { public static void main(String[] args)throws Exception{

    ForkJoinPool pool = new ForkJoinPool();
    //提交可分解的PrintTask任务
    pool.submit(new PrintTask(0, 300));
    pool.awaitTermination(2, TimeUnit.SECONDS);
    //关闭线程池
    pool.shutdown();
    }
    }

    有返回值的“大任务”:

    //继承RecursiveTask来实现“可分解”的任务
    class CalTask extends RecursiveTask<Integer>{
    //每个“小任务”最多只累加20个数
    private static final int THRESHOLD = 20;
    private int arr[];
    private int start;
    private int end;
    //累加从start到end数组
    public CalTask(int[] arr, int start, int end){
    this.arr = arr;
    this.start = start;
    this.end = end;
    }
    @Override
    protected Integer compute() {
    int sum = 0;
    // 当end与start之间的差小于THRESHOLD时,开始进行实际累加
    if(end - start < THRESHOLD){
    for(int i = start; i < end; i++){
    sum += arr[i];
    }
    return sum;
    }else{
    //当end与start之间的差大于THRESHOLD,既要累加的数超过20个时,将大任务分解成两个小任务
    int middle = (start + end) / 2; CalTask left = new CalTask(arr, start, middle);
    CalTask right = new CalTask(arr, middle, end);
    //并执行两个小任务 left.fork();
    right.fork();
    //把两个小任务累加的结果合并起来
    return left.join() + right.join();
    }
    }

    }
    public class Sum {
    public static void main(String[] args)throws Exception{
    int[] arr = new int[100]; Random rand = new Random();
    int total = 0;
    //初始化100个数字元素
    for(int i = 0, len = arr.length; i < len; i++){
    int tmp = rand.nextInt(20);
    //对数组元素赋值,并将数组元素的值添加到sum总和中
    System.out.println(tmp);
    total += (arr[i] = tmp);
    }
    System.out.println("初始化统计出的和:" + total);
    //创建一个通用池
    ForkJoinPool pool = ForkJoinPool.commonPool();
    //提交可分解的CaltTask任务
    Future<Integer> future = pool.submit(new CalTask(arr, 0, arr.length));
    System.out.println("使用CalTask计算出的和:" + future.get());
    //关闭线程
    pool.shutdown();
    }
    }
  • 相关阅读:
    题解:2018级算法第五次上机 C5-图2
    题解:2018级算法第四次上机 C4-最小乘法
    题解:2018级算法第四次上机 C4-商人卖鱼
    题解:2018级算法第三次上机 C3-Zexal的浩瀚星辰
    C语言算法动态规划板子题汇总
    QT样式表
    3.PCB-禁止布线层
    2.PCB-板切割槽
    1.PCB-板形设置
    变压器
  • 原文地址:https://www.cnblogs.com/jpfss/p/9059301.html
Copyright © 2011-2022 走看看