java多线程——定时器、线程池

举报
王同学要努力 发表于 2022/09/22 20:35:51 2022/09/22
【摘要】 java多线程——定时器、线程池

定时器

定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码.

定时器是一种实际开发中非常常用的组件.
比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连.
比如一个 Map, 希望里面的某个 key 在 3s 之后过期(自动删除).
类似于这样的场景就需要用到定时器.

标准库中的定时器

  • 标准库中提供了一个 Timer 类. Timer 类的核心方法为schedule .

  • schedule 包含两个参数. 第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后- 执行 (单位为毫秒).

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        System.out.println("hello");
   }
}, 3000);
## 定时器的构成:

  • 一个带优先级的阻塞队列

  • 队列中的每个元素是一个 Task 对象.

  • Task 中带有一个时间属性, 队首元素就是即将

  • 同时有一个 worker 线程一直扫描队首元素, 看队首元素是否需要执行


import java.util.concurrent.PriorityBlockingQueue;

class  MyTask implements  Comparable<MyTask>{
    //任务要具体干啥
    private  Runnable runnable;

    // 任务具体啥时候干. 保存任务要执行的毫秒级时间戳
    private  long time ;

    //after 是一个时间间隔 不是绝对的时间戳的值

    public  MyTask(Runnable runnable, long delay){
        this.runnable = runnable;
        this.time = System.currentTimeMillis()+delay;
    }

    public  void run(){
        runnable.run();
    }

    public long getTime() {
        return time;
    }

    @Override
    public int compareTo(MyTask o) {
        return  (int)(this.time - o.time);
    }


}
class MyTimer {
    //定时器内部要能够存放多个任务
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();

    public void shedule(Runnable runnable, long delay) {
        MyTask task = new MyTask(runnable, delay);
        queue.put(task);
        // 每次任务插入成功之后, 都唤醒一下扫描线程, 让线程重新检查一下队首的任务看是否时间到要执行~~
        synchronized (locker) {
            locker.notify();

        }
    }


    private Object locker = new Object();

    public MyTimer() {
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    //先取出首元素
                    MyTask task = queue.take();
                    // 再比较一下看看当前这个任务时间到了没?
                    long curTime = System.currentTimeMillis();
                    if (curTime < task.getTime()) {
                        //时间没到 把任务再塞回队列中
                        queue.put(task);
                        //给定一个指定时间
                        synchronized (locker) {
                            locker.wait(task.getTime() - curTime);
                        }
                    } else {
                        //时间到了 执行这个任务
                        task.run();
                    }


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

        });
        t.start();

    }
}



public class demo4 {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.shedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello timer!");
            }
        },5000);
        System.out.println("main");

    }
}

image.png

最先在控制台输出 main 然后过5秒输出hello timer!

线程池

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

线程池的优点:

1、降低资源消耗:减少线程的创建和销毁带来的性能开销。
2、提高响应速度:当任务来时可以直接使用,不用等待线程创建
3、可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象。

标准库中的线程池

使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
返回值类型为 ExecutorService
通过 ExecutorService.submit 可以注册一个任务到线程池中.

ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(new Runnable() {
    @Override
    public void run() {
        System.out.println("hello");
   }
});

实现线程池

核心操作为 submit, 将任务加入线程池中
使用 Worker 类描述一个工作线程. 使用 Runnable 描述一个任务.
使用一个 BlockingQueue 组织所有的任务
每个 worker 线程要做的事情: 不停的从 BlockingQueue 中取任务并执行.
指定一下线程池中的最大线程数 maxWorkerCount; 当当前线程数超过这个最大值时, 就不再新增

实现线程池

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;

class MyThreadPoll{
    //线程池
    private BlockingQueue<Runnable> queue = new LinkedBlockingDeque<>();

    public  void sumit(Runnable runnable)throws InterruptedException{
        queue.put(runnable);
    }
    public  MyThreadPoll(int m){
        for(int i = 0;i < m;i++){
            Thread t =new Thread(()->{
               while (true){
                   try {
                       Runnable runnable = queue.take();
                       runnable.run();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }

               }
            });
            t.start();

        }
    }

}

public class demo5 {
    public static void main(String[] args) throws InterruptedException {
         MyThreadPoll poll = new MyThreadPoll(10);
         for(int i = 0;i < 1000;i++){
             int taskId = i;
             poll.sumit(new Runnable() {
                 @Override
                 public void run() {
                     System.out.println("执行当前任务:"+taskId+"当前线程"+Thread.currentThread().getName());
                 }
             });
         }
    }
}

image.png

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。