二 Java利用等待/通知机制实现一个线程池

九章 等级 740 0 0
标签: 线程池Java

接着上一篇博客的 一Java线程的等待/通知模型 ,没有看过的建议先看一下。下面我们用等待通知机制来实现一个线程池.

本文的代码放到了github上,地址如下: git@github.com:jiulu313/ThreadPool.git

线程的任务就以打印一行文本来模拟耗时的任务。主要代码如下:

1 定义一个任务的接口。

  /* 任务的接口 3  */
 public interface Task { 
     void doSomething(); 
}

2 实现一个具体的任务。

/*
 * 具体的任务
 */
public class PrintTask implements Task{

    //打印一句话,睡一秒,来模拟耗时的任务
    @Override
    public void doSomething() {
        System.out.println("任务:"+Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3 实现工作线程

    /*
 * 工作者线程
 */
public class Worker implements Runnable {
    //线程是否正在运行
    private boolean running = true;

    //保存Thread,方便start()
    private Thread thread;

    //保存线程池的任务队列,作同步用
    private LinkedList<Task> tasks;

    public void setThread(Thread thread) {
        this.thread = thread;
    }

    public void setTasks(LinkedList<Task> tasks) {
        this.tasks = tasks;
    }

    //启动此工作线程
    public void start() {
        if (thread != null) {
            thread.start();
        }
    }

    // 关闭此工作线程
    public void shutDown() {
        running = false;
        thread.interrupt();
    }

    @Override
    public void run() {
        while (running) {
            Task task = null;

            //对共享变量加锁,此处为任务队列,因为会有多个线程访问
            synchronized (tasks) {

                //当条件不满足时,线程等待,见上一篇博文
                while (tasks.isEmpty()) {
                    try {
                        //线程进入等待状态,并且释放锁
                        tasks.wait();
                    } catch (InterruptedException e) {
                        //感知到外部对此线程的中断操作
                        Thread.currentThread().interrupt();
                        return;
                    }
                }

                //条件满足
                task = tasks.removeFirst();
            }

            //执行任务
            if (task != null) {
                task.doSomething();
            }
        }
    }
}

4 创建一个线程池

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class DefaultThreadPool implements ThreadPool {
    private int maxWorksNum = 10;
    private int minWorksNum = 1;
    private int defaultWorksNum = 5;

    // 任务列表
    private LinkedList<Task> tasks = new LinkedList<>();

    // 工作线程列表
    private LinkedList<Worker> workers = new LinkedList<>();

    //工作线程个数
    private int workerNum = defaultWorksNum;


    @Override
    public void excute(Task task) {
        // 添加一个工作,然后进行通知
        if (task != null) {
            synchronized (tasks) {
                //添加到最后一个位置
                tasks.addLast(task);
                //通知等待的线程,有新的任务了
                tasks.notify();
            }
        }
    }

    // 关闭线程池
    @Override
    public void shutDown() {
        for (Worker worker : workers) {
            worker.shutDown();
        }
    }

    // 初始化工作者线程
    public void initWorkers(int num) {
        if (num > maxWorksNum) {
            num = maxWorksNum;
        } else if (num < minWorksNum) {
            num = minWorksNum;
        } else {
            num = defaultWorksNum;
        }

        for (int i = 0; i < workerNum; i++) {
            //创建工作线程
            Worker worker = new Worker();

            //添加到工作队列
            workers.add(worker);

            //新建一个线程对象,并将worker赋值
            Thread thread = new Thread(worker);

            //设置线程对象,作启动,中断用
            worker.setThread(thread);

            //设置任务队列,作同步用
            worker.setTasks(tasks);
        }
    }

    // 启动线程池
    public void start(){
        if(workers != null){
            for(Worker worker : workers){
                //启动一个工作线程
                worker.start();
            }
        }
    }

    // 新增加工作线程,但是不能大于线程池最大线程数
    @Override
    public void addWorkers(int num) {
        if (num <= 0) {
            return;
        }

        int remain = maxWorksNum - workerNum;
        if (num > remain) {
            num = remain;
        }

        for (int i = 0; i < num; i++) {
            Worker worker = new Worker();
            workers.add(worker);
            Thread thread = new Thread(worker);
            thread.start();
        }

        workerNum = workers.size();
    }

    // 减少工作线程,至少留1个,不能减少到0
    @Override
    public void removeWorkers(int num) {
        if(num >= workerNum || num <= 0){
            return;
        }

        for(int i =0;i<num;i++){
            Worker worker = workers.getLast();
            worker.shutDown();
        }

        workerNum = workers.size();
    }

    @Override
    public int getTaskSize() {
        return tasks.size();
    }


}

5 新建测试类

public class ThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
        //1 新建一个线程池
        DefaultThreadPool pool = new DefaultThreadPool();

        //2 设置线程池的大小为5
        pool.initWorkers(5);

        //3 启动线程池
        pool.start();

        //4 往任务队列里面添加任务
        for(int i = 0;i<100;i++){
            pool.excute(new PrintTask());
        }

    }
}

在eclipse中运行,结果部分截图如下:

二 Java利用等待/通知机制实现一个线程池

好了,一个线程池就这样,这只是一个小例子,提示线程池的原理

其实实现工作中,一个线程池要考虑的问题远比这个多,也更复杂。

其中比较重要的两个方面:

1 线程池开几个线程为最合适?

我们知道,线程不是开的越多越好,而要根据业务的场景,硬件的指标,带宽的大小等等

一般线程池的个数为CPU核心数的个数加1 ,google的建议。此外可能还要具体分析业务

大,中,小的业务需求,也是不一样的。

大任务:比如下载一部电影,可能要十几分钟甚至几十分钟的任务

中任务:比如下载一幅图片,有1M以上了到十几M的大小的。

小任务:比如下载的是游戏的ico,就十几K的到1M以下的。

小任务可以多开几个线程。

中任务的可以保守点。

大任务的尽量不要开的线程太多

具体值还需要看具体业务,具体场景。这些只是建议。

2 线程用哪种队列,也是和上面有关系。

今天就到这了,后续还会抽时间研究线程并发这块,希望对大家有帮忙。

收藏
评论区

相关推荐

二 Java利用等待/通知机制实现一个线程池
接着上一篇博客的 一Java线程的等待/通知模型(http://www.cnblogs.com/start1225/p/5866575.html "一 java线程的等待/通知模型")  ,没有看过的建议先看一下。下面我们用等待通知机制来实现一个线程池. (https://www.helloworld.net/p/XJXfgbimvcjd) 本
面试官突击一问:深入理解mysql技术
京东Java研发岗一面(基础面,约1小时) 自我介绍,主要讲讲做了什么和擅长什么 springmvc和springboot区别 @Autowired的实现原理 Bean的默认作用范围是什么?其他的作用范围? 索引是什么概念有什么作用?MySQL里主要有哪些索引结构?哈希索引和B+树索引比较? Java线程池的原理?线程池有哪些?线程池
(CSDN 迁移) JAVA多线程实现
前几篇文章中分别介绍了 单线程化线程池(newSingleThreadExecutor) 可控最大并发数线程池(newFixedThreadPool) 可回收缓存线程池(newCachedThreadPool) newScheduledThreadPool用于构造安排线程池,能够根据需要安排在给定延迟后运行命令或者定期地执行。 在JAVA文档的介绍
JAVA线程池ThreadPoolExecutor与阻塞队列BlockingQueue .
从Java5开始,Java提供了自己的线程池。每次只执行指定数量的线程,java.util.concurrent.ThreadPoolExecutor 就是这样的线程池。以下是我的学习过程。 首先是构造函数签名如下: **\[java\]** [view plain](http://my.oschina.net/u/1398304/admin/#)
JAVA线程池(二)
Executor 执行 Runnable 任务 ----------------------- 通过 Executors 的以上四个静态工厂方法获得 ExecutorService 实例,而后调用该实例的 execute(Runnable command)方法即可。一旦 Runnable 任务传递到 execute()方法,该方法便会自动在一个线程上执行。
Java 线程池
线程池用的比较多。参考文章http://blog.csdn.net/sd0902/article/details/8395677 线程的优点 1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。 2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程
Java中常量池详解
在Java的内存分配中,总共3种常量池: 转发链接 [:https://blog.csdn.net/zm13007310400/article/details/77534349](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fblog.csdn.net%2Fzm13007310400%2
Java基础教程——线程池
启动新线程,需要和操作系统进行交互,成本比较高。 使用线程池可以提高性能—— > 线程池会提前创建大量的空闲线程,随时待命执行线程任务。在执行完了一个任务之后,线程会回到空闲状态,等待执行下一个任务。(这个任务,就是Runnable的run()方法,或Callable的call()方法)。 * * * Java 5之前需要手动实现线程池,Java 5之
Java多线程之线程池7大参数、底层工作原理、拒绝策略详解
Java多线程之线程池7大参数详解 目录 企业面试题 线程池7大参数源码 线程池7大参数详解 底层工作原理详解 线程池的4种拒绝策略理论简介 面试的坑:线程池实际中使用哪一个? 1\. 企业面试题 线程池的工作原理,几个重要参数,然后给了具体几个参数分析线程池会怎么做,最 后问阻塞队列用是什么? 线程池的构造类的方
java 线程池入门
一简介 === 线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的。在jdk1.5之后这一情况有了很大的改观。Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用。为我们在开发中处理线程的问题提供了非常大的帮助。 二:线程池 =====
java 面试知识点笔记(十三)多线程与并发
**java线程池**,利用Exceutors创建不同的线程池满足不同场景需求: 1. newSingleThreadExecutor() 创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。 2.
java_线程池
血一样的教训,今天上午参加了一家现场面试java。在这之前,我一直认为我的java基础还是可以的,而今天一问三不知。现在将面试的问题整理出来 **一、说说java中的线程池?**   1.线程池:线程池是线程的集合,不用自己创建线程,把线程直接给线程池,由线程池处理。       2.过程:首先,使用线程池可以重复利用已有的线程继续执行任务,避免线程在
java各种面试问题
二、Java多线程相关 ----------- * 线程池的原理,为什么要创建线程池?创建线程池的方式; * 线程的生命周期,什么时候会出现僵死进程; * 说说线程安全问题,什么实现线程安全,如何实现线程安全; * 创建线程池有哪几个核心参数? 如何合理配置线程池的大小? * volatile、ThreadLocal的使用场景和原理;
2020年首发70道阿里巴巴高级Java开发面试题(带详细答案)
**2020年首发70道阿里巴巴高级Java开发面试题(带详细答案)**![在这里插入图片描述](https://img-blog.csdnimg.cn/2020102520221477.png#pic_center) 面试题 === 1、java事件机制包括哪三个部分?分别介绍。 2、为什么要使用线程池? 3、线程池有什么作用? 4、说说几种常见
2020年首发70道阿里巴巴高级Java开发面试题(带详细答案)
**2020年首发70道阿里巴巴高级Java开发面试题(带详细答案)**![在这里插入图片描述](https://img-blog.csdnimg.cn/2020102520221477.png#pic_center) 面试题 === 1、java事件机制包括哪三个部分?分别介绍。 2、为什么要使用线程池? 3、线程池有什么作用? 4、说说几种常见