手写线程池

6 篇文章 0 订阅
3 篇文章 0 订阅

线程成本

首先我们需要了解线程是不是越多越好?

1、线程在Java中是一个对象,每一个Java线程都需要一个操作系统线程支持。线程创建、销毁需要时间。如果创建时间+销毁时间>执行任务时间就很不合算。

2、Java对象占用堆内存,操作系统线程占用系统内存,根据JVM规范,一个线程默认最大栈大小1M,这个栈空间是需要从系统内存中分配的。线程过多,会消耗很多的内存。

从上面我们知道,线程创建,销毁需要时间,线程创建需要占用系统内存。线程的存在是有成本的,这就要看这个成本会不会影响系统性能了。

 

线程池构成

线程池中有任务,有线程。线程会把任务放入线程中执行。

 

如上图,线程池会接收任务,将任务放入仓库中;然后线程会从仓库中取任务,把那个将任务运送至工作内存中执行。当没有任务时,线程阻塞,有任务时线程被唤醒。

自定义线程池

我们需要一个集合用于存放线程。

1
2
//1. 多个线程组成
private List<Thread> workers;

然后再创建一个仓库,这个仓库是一个阻塞队列。

1
2
//2.仓库
private BlockingQueue<Runnable> queue;

需要一个开关来控制线程。

1
private volatile boolean isWorking = true;

编写线程类的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//poolSize: 工作线程数量
//taskSize: 任务数量
public FixedSizeThreadPool(int poolSize, int taskSize) {

    if (poolSize <= 0 || taskSize <= 0) {
        throw new IllegalArgumentException("非法参数");
    }

    this.queue = new LinkedBlockingQueue<>(taskSize);
    //线程安全的list
    this.workers = Collections.synchronizedList(new ArrayList<>());
    //创建线程,将线程放入集合中
    for (int i = 0; i < poolSize; i++) {
        Worker worker = new Worker(this);
        worker.start();
        workers.add(worker);
    }
}

创建一个线程类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//3.具体线程
public static class Worker extends Thread {
    private FixedSizeThreadPool pool;

    public Worker(FixedSizeThreadPool pool) {
        this.pool = pool;
    }

    @Override
    public void run() {
        while (pool.isWorking || pool.queue.size() > 0) {
            Runnable task = null;

            try {
                if (pool.isWorking) {
                    task = this.pool.queue.take();//阻塞方式拿,拿不到会一直等待
                } else {
                    //非阻塞操作
                    task = this.pool.queue.poll();
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (task != null) {
                task.run();
                System.out.println("线程:" + Thread.currentThread().getName() + "执行完毕");

            }
        }
    }
}

我们再编写一个执行任务的方法。

1
2
3
4
5
6
7
8
public boolean execute(Runnable runnable) {
    if (isWorking) {
        //往阻塞队列中添加任务
        return this.queue.offer(runnable);
    }

    return false;
}

关闭线程池。

1
2
3
4
5
6
7
8
9
public void shutDown() {
    this.isWorking = false;

    for(Thread thread : workers){
       if (Thread.State.BLOCKED.equals(thread.getState())) {
           thread.interrupt();//中断线程
       }
    }
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public  void testPool() {
    int taskSize = 6;
    FixedSizeThreadPool fixedSizeThreadPool = new FixedSizeThreadPool(3, taskSize);
    for (int i = 0; i < taskSize; i++) {
        fixedSizeThreadPool.execute(() -> {
            System.out.println("任务被放入了仓库");
            try {
                Thread.sleep(2000L);
            } catch (InterruptedException e) {
                System.out.println("线程中断");
            }
        });
    }

    fixedSizeThreadPool.shutDown();
}

上面的代码并不完善,有兴趣的可以自行完善。项目地址FixedSizeThreadPool.java

线程池数量多少合适

如果是计算型任务?

cpu数量的1-2倍

如果是IO型任务?

则需多一些线程,要根据具体的IO阻塞时长进行考量决定。如tomcat中默认的最大线程数为:200
也可考虑根据需要在一个最小数量和最大数量间自动增减线程数。

往期推荐

扫码二维码,获取更多精彩。或微信搜Lvshen_9,可后台回复获取资料

1.回复"java" 获取java电子书;


2.回复"python"获取python电子书;


3.回复"算法"获取算法电子书;


4.回复"大数据"获取大数据电子书;


5.回复"spring"获取SpringBoot的学习视频。


6.回复"面试"获取一线大厂面试资料


7.回复"进阶之路"获取Java进阶之路的思维导图


8.回复"手册"获取阿里巴巴Java开发手册(嵩山终极版)


9.回复"总结"获取Java后端面试经验总结PDF版


10.回复"Redis"获取Redis命令手册,和Redis专项面试习题(PDF)


11.回复"并发导图"获取Java并发编程思维导图(xmind终极版)

另:点击【我的福利】有更多惊喜哦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值