java 线程池的使用及原理(一):线程池的使用

大家在一开始接触多线程的时候,毫无疑问是用Thread的start()来创建启动一个线程。事实上,线程的创建与销毁是非常消耗时间与系统资源的,甚至比实际需求的消耗还要多。

那么问题来了,我应该怎么正确的使用多线程呢?
java.util.concurrent 包下的 Executors 可以帮我们创建一个线程池,只需传入相应的参数即可。线程池的原理其实就是对多线程的一个管理,为了实现异步机制的一种方法,其实就是多个线程执行多个任务,任务执行完毕,释放对线程的使用权,达成线程复用的效果。

线程池的使用

  • 先会用,后看原理,是我不知道哪里听来的道理
  • java 为我们提供了 5 种类型的线程池:newSingleThreadExecutor、newFixedThreadPool、newCachedThreadPool、newScheduledThreadPool、newWorkStealingPool

1、newSingleThreadExecutor

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NewSingleThreadExecutorTest {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for(int i = 0; i < 5; i++) {
            executorService.execute(() -> {
                System.out.println("当前执行线程是 + \t" + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }
}
  • 可以看到,一直是同一个线程在执行
    在这里插入图片描述

2、newFixedThreadPool

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NewFixedThreadPoolTest {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for(int i = 0; i < 9; i++) {
            executorService.execute(() -> {
                System.out.println("当前执行线程是 + \t" + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }
}
  • 三个线程在固定切换
    在这里插入图片描述

3、newCachedThreadPool

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NewCachedThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for(int i = 0; i < 5; i++) {
            executorService.execute(() -> {
                System.out.println("当前执行线程是 + \t" + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        Thread.sleep(10000);
        System.out.println("================ Cache 复用 ================");
        for(int i = 0; i < 5; i++) {
            executorService.execute(() -> {
                System.out.println("当前执行线程是 + \t" + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        executorService.shutdown();
    }
}
  • 需要多少,创建多少,一定时间内可以复用
    在这里插入图片描述

4、newScheduledThreadPool

  • 周期性的执行任务,那么会存在一个问题,间隔时间与任务执行时间
  • scheduleAtFixedRate 当任务执行时间大于间隔时间时,会立刻执行。简单来说,我的间隔时间其实是包含任务执行时间
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class NewScheduledThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "\t " + "每2秒执行一次");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis());
            }
        }, 1, 2, TimeUnit.SECONDS);
//        executorService.shutdown();
    }
}
  • scheduleWithFixedDelay:不管任务执行时间多久,我只管任务与任务的间隔时间正确。简单来说,我的间隔时间其实是不包含任务执行时间
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class NewScheduledThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        executorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "\t " + "每2秒执行一次");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis());
            }
        }, 1, 2, TimeUnit.SECONDS);
        // executorService.shutdown();
    }
}

5、newWorkStealingPool

  • 多个任务分成多个队列完成
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NewWorkStealingPoolTest {
    private static final int threads = 10;
    public static void main(String[] args) throws InterruptedException {
        // 用于计数线程是否执行完成
        CountDownLatch countDownLatch = new CountDownLatch(threads);
        System.out.println("---- start ----");
        ExecutorService executorService = Executors.newWorkStealingPool(3);
        for (int i = 0; i < threads; i++) {
            executorService.execute(() -> {
                try {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName());
                } catch (Exception e) {
                    System.out.println(e);
                } finally {
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        System.out.println("---- end ----");
    }
}

在这里插入图片描述

总结

  • newSingleThreadExecutor:单一线程来执行任务,保证任务是按照指定顺序执行;
  • newFixedThreadPool:创建固定大小的线程池,可控制线程最大的并发数,超出的线程会在队列中等待;
  • newCachedThreadPool:无限扩大的线程池,需要就创建,一定时间后自动回收;
  • newScheduledThreadPool:可以延时启动,执行间隔时间相同的任务;
  • newWorkStealingPool:将任务分解成多个任务队列,缓冲线程池的压力;

execute() 和 submit() 的区别

  • execute() 是 void 方法,没有返回值
  • submit 有三个重载方法
    • public Future<?> submit(Runnable task); // 不带返回值使用
    • public Future submit(Runnable task, T result); // 在调用方法时,确定返回值
    • public Future submit(Callable task); // 在Callable内返回需要的值
public class ExecuteAndSubmit {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 5,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(1000));

        threadPool.execute(() -> {
            System.out.println("我是 execute,我没有返回值,需要返回值请使用 submit");
        });

        Future<?> submit2 = threadPool.submit(() -> {
            System.out.println("我是submit,但传入的是 (Runnable)");
        });
        System.out.println("我是没有类型的返回值" + submit2.get());

        Future<Integer> submit = threadPool.submit(() -> {
            System.out.println("我是submit,但传入的是 (Callable)");
            return 10;
        });
        System.out.println("Callable 返回值" + submit.get());

        Future<Integer> submit1 = threadPool.submit(() -> {
            System.out.println("我是submit,但传入的是 (Runnable,T)");
        }, 1000);
        System.out.println("我是submit,但传入的是 (Runnable),但同时传入了一个返回值" + submit1.get());

        threadPool.shutdown();
    }
}

在这里插入图片描述

线程池方法使用示例

  • 这里我们使用自定义的线程池
  • allowCoreThreadTimeOut:允许核心线程超时,即线程池不调用shutdown也可以自动关闭
  • prestartCoreThread:核心线程预创建,true 表示创建成功,这里只会预创建一个
  • prestartAllCoreThreads:预创建设置的核心线程,返回预创建的个数
  • getActiveCount:获取当前活跃的线程个数
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class InstanceMethodsTest {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 10,
                1L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(2));
        int activeCount = threadPool.getActiveCount();
        System.out.println("刚创建完线程池的 activeCount" + activeCount);
        System.out.println("============== 开始预创建线程 ==============");
        /* 线程池核心线程允许超时设置 */
        threadPool.allowCoreThreadTimeOut(true);
        boolean flag = threadPool.prestartCoreThread();
        System.out.println("prestartCoreThread 预创建单个线程是否成功" + flag);
        int prestartAllCoreThreads = threadPool.prestartAllCoreThreads();
        System.out.println("prestartAllCoreThreads: 创建全部核心线程" + prestartAllCoreThreads);
        int activeCount1 = threadPool.getActiveCount();
        System.out.println("preCoreThread预创建核心线程的 activeCount1: " + activeCount1);


        System.out.println("============== 核心数已满,再来预创建线程 ==============");
        boolean flag1 = threadPool.prestartCoreThread();
        System.out.println("prestartCoreThread 预创建单个线程是否成功" + flag1);
        int prestartAllCoreThreads1 = threadPool.prestartAllCoreThreads();
        System.out.println("prestartAllCoreThreads: " + prestartAllCoreThreads1);
        int activeCount2 = threadPool.getActiveCount();
        System.out.println("preCoreThread预创建核心线程的 activeCount1: " + activeCount2);
    }
}

在这里插入图片描述

  • 监控线程池状态
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class InstanceMethodsTest01 {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(50, 100,
                1L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(1000));
        for(int i = 1; i <= 1000; i++) {
            threadPool.execute(() -> {
                try {
                    Thread.sleep(1000);
                    System.out.print("0");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        while (true) {
            System.out.println();

            int queueSize = threadPool.getQueue().size();
            System.out.println("当前排队线程数getQueue:" + queueSize);

            int activeCount = threadPool.getActiveCount();
            System.out.println("当前活动线程数getActiveCount:" + activeCount);


            long completedTaskCount = threadPool.getCompletedTaskCount();
            System.out.println("执行完成线程数getCompletedTaskCount:" + completedTaskCount);

            long taskCount = threadPool.getTaskCount();
            System.out.println("总线程数getTaskCount:" + taskCount);

            Thread.sleep(3000);
        }

    }
}
  • 输出如下:
    • getCompletedTaskCount:获取已完成任务大致总数, 不一定是实时,因为操作完成,记录也需要时间
当前排队线程数getQueue:950
当前活动线程数getActiveCount:50
执行完成线程数getCompletedTaskCount:0
总线程数getTaskCount:1000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0当前排队线程数getQueue:812
当前活动线程数getActiveCount:50
执行完成线程数getCompletedTaskCount:139
总线程数getTaskCount:1000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
当前排队线程数getQueue:695
当前活动线程数getActiveCount:50
执行完成线程数getCompletedTaskCount:255
总线程数getTaskCount:1000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
当前排队线程数getQueue:545
当前活动线程数getActiveCount:50
执行完成线程数getCompletedTaskCount:405
总线程数getTaskCount:1000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
当前排队线程数getQueue:395
当前活动线程数getActiveCount:50
执行完成线程数getCompletedTaskCount:555
总线程数getTaskCount:1000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
当前排队线程数getQueue:245
当前活动线程数getActiveCount:50
执行完成线程数getCompletedTaskCount:705
总线程数getTaskCount:1000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
当前排队线程数getQueue:95
当前活动线程数getActiveCount:50
执行完成线程数getCompletedTaskCount:855
总线程数getTaskCount:1000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
当前排队线程数getQueue:0
当前活动线程数getActiveCount:0
执行完成线程数getCompletedTaskCount:1000
总线程数getTaskCount:1000

当前排队线程数getQueue:0
当前活动线程数getActiveCount:0
执行完成线程数getCompletedTaskCount:1000
总线程数getTaskCount:1000

当前排队线程数getQueue:0
当前活动线程数getActiveCount:0
执行完成线程数getCompletedTaskCount:1000
总线程数getTaskCount:1000

Process finished with exit code -1
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值