JavaEE_多线程(环形队列单消费者,单生产者模型、定时器、线程池)

1.生产者,消费者模型

大致设计思路:

  1. 通过阻塞队列完成生产者,消费者的解耦
  2. 阻塞队列中的put和take方法可能会出现阻塞。
  3. put方法在唤醒队列满的时候要阻塞,take方法在唤醒队列为空的时候要阻塞。
  4. put方法后破坏了take方法的阻塞条件,唤醒take方法
  5. take方法破坏了put方法的阻塞条件,唤醒put方法

在这里插入图片描述

package 阻塞队列.生产者消费者模型;

class MyBlockQueue {
    private int[] array = new int[10];
    private int head = 0;
    private int till = 0;
    private int size = 0;
    Object locker = new Object();

    //put入队列
    public void put(int num) throws InterruptedException {
        synchronized(locker) {
            while (size == array.length) {
                //阻塞等待
                System.out.println("队列已满,阻塞等待");
                locker.wait();
            }
            array[till++] = num;
            size++;
            if (till >= array.length) {
                till = 0;
            }
            //唤醒take线程
            locker.notify();
        }
    }

    //take出队列
    public int take() throws InterruptedException {
        synchronized (locker) {
            if (size == 0) {
                System.out.println("队列为空,阻塞等待");
                locker.wait();
            }
            int pop = array[head];
            head++;
            if (head >= array.length) {
                head = 0;
            }
            size--;
            locker.notify();//唤醒put线程
            return pop;
        }
    }
}
package 阻塞队列.生产者消费者模型;

public class TestProducerDemo {
    public static void main(String[] args) {
        MyBlockQueue queue=new MyBlockQueue();
        //消费者
        Thread customer=new Thread(){
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                        System.out.println("消费元素+ " + queue.take());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        customer.start();

        //生产者
        Thread producer =new Thread(){
            public void run(){
                for(int i=0;i<50;i++){
                    try {
                        queue.put(i);
                        System.out.println("生产元素 "+i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        producer.start();

        try {
            producer.join();
            customer.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行截图
在这里插入图片描述

2.定时器

Java定时器中可以安排很多任务
这些任务会按照时间顺序,谁先到谁执行

定时器内部使用阻塞优先队列来组织任务,内部提供schedule方法,让调用者向定时器中插入任务。
此外,定时器内部也存在一个线程,这个线程的工作是扫描队头元素,调查这个任务的时间是否就绪,如果就绪则执行这个任务,否则塞回队列中继续执行。

需要特别注意的是:为了避免CPU出现忙等的状态,浪费CPU资源,这里引入wait和notify来节约CPU效率。

当没有到时间的时候进行wait等待,等待的时间可以通过用户的输入计算。当重新添加元素的时候又要唤醒线程进行重新判断任务是否要执行。

package 定时器;

import java.util.concurrent.PriorityBlockingQueue;

class Task implements Comparable<Task>{//描述任务
    private Runnable command;//执行的任务
    public long time;//这个任务多少时间要在什么时候执行

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

    public Task(Runnable run, long time) {
        this.command = run;
        this.time = System.currentTimeMillis() + time;
    }

    //指定优先队列的优先级
    @Override
    public int compareTo(Task o) {
        return (int)(this.time-o.time);
    }
}

class MyTimer {
    private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();//组织任务

    public void schedule(Runnable command, long time) {
        Task task = new Task(command, time);
        queue.put(task);
        synchronized (locker){
            locker.notify();//重新添加任务时唤醒线程,保证新加入的任务不会错过。
        }
    }

    private Object locker=new Object();//解决忙等问题

    public MyTimer() {
        //创建线程,扫描队列。
        Thread thread = new Thread() {
            public void run() {
                while (true) {
                    try {
                        Task task = queue.take();
                        long curTime = System.currentTimeMillis();
                        if (task.time > curTime) {
                            //时间未到
                            queue.put(task);
                            synchronized(locker) {
                                locker.wait(task.time-curTime);
                            }
                        } else {
                            //时间到了
                            task.run();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        break;
                    }
                }
            }
        };
        thread.start();
    }
}
package 定时器;

public class TestTimer {
    public static void main(String[] args) {
        MyTimer timer=new MyTimer();
        System.out.println("开始运行");
        timer.schedule(new Runnable(){
            public void run(){
                System.out.println("Hello Timer");
            }
        },3000);
    }
}

在这里插入图片描述
代码运行先打印开始执行,等三秒后打印Hello Timer

3. 线程池

在使用线程前,先创建一批线程。在需要线程的时候从这些线程中拿即可。这样的话效率就比创建销毁线程的效率更高。

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue)

参数意义

  1. corePoolSize - 即使空闲时仍保留在池中的线程数,除非设置allowCoreThreadTimeOut
  2. maximumPoolSize - 池中允许的最大线程数
  3. keepAliveTime - 当线程数大于核心时,这是多余的空闲线程在终止之前等待新任务的最大时间。
    unit - keepAliveTime参数的时间单位
  4. workQueue - 在执行任务之前用于保存任务的队列。 该队列将仅保存execute方法提交的Runnable任务。(阻塞队列,描述了线程池要执行的任务)
  5. RejectedExecutionHandler handler 拒绝策略,当任务队列已满时应该进行的操作,处理极端情况。

标注库提供了一些工厂方法,可以创建出不同种风格的线程池实例。
6. . newFixedThreadPool:创建出一个固定线程数量的线程池.(完全没有临时线程的版本)
7. newCachedThreadPool:创建出一个数量可变的线程池.(完全没有正式线程,全是临时线程)
8. newSingleThreadPool:创建出一个只包含一个线程的线程池.(只是特定场景下使用)
9. newScheduleThreadPool:能够设定延时时间的线程池. (插入的任务能够过一会再执行),相当于进阶版的定时器.

eg:

package 线程池;

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

public class TestPool {
    public static void main(String[] args) {
        //创建线程池实例
        ExecutorService service= Executors.newFixedThreadPool(10);
        //注册一些方法
        for(int i=0;i<20;i++) {
            service.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("run");
                }
            });
        }
    }
}

自己定义线程池:

  1. 描述任务使用Runnable
  2. 组织任务使用阻塞队列
  3. 提前创建一批线程
package 线程池;

import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

class MyThreadPool {
    //工作线程
    class workThread extends Thread {
        //从队列中取任务
        private  BlockingQueue<Runnable>queue;
        public workThread(BlockingQueue<Runnable> queue) {
            this.queue=queue;
        }

        public void run(){
            while(true){
                try {
                    Runnable task=queue.take();
                    task.run();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //阻塞队列组织任务
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();

    private ArrayList<workThread> list = new ArrayList<>();

    private static final int MAX_WORK_SIZE = 10;

    public void submit(Runnable command) {
        try {
            if (list.size() < MAX_WORK_SIZE) {
                //线程数量不足时,创建新线程,并启动
                workThread work = new workThread(queue);
                work.start();
                list.add(work);
            }
            queue.put(command);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试代码:

package 线程池;

public class TestPool {
    public static void main(String[] args) {
        //创建线程池实例
        MyThreadPool threadPool=new MyThreadPool();
        for(int i=0;i<20;i++){
            threadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("MyThreadPool");
                }
            });
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NUC_Dodamce

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值