【JavaEE学习日记】----多线程案例下

目录

1.定时器

 2.自己实现一个定时器

2.1执行任务

 2.2组织任务

 2.3执行时间到了的任务

 执行结果:

完整代码:

3.线程池

4.自己实现一个线程池


1.定时器

定时器类似于一个闹钟,进行定时,在一定时间后,被唤醒并执行某个之前设定好的任务

java标准库中提供了一个定时器(java.util.Timer),核心方法只有一个schedulle,有两个参数:任务是啥,多长时间后执行

import java.util.Timer;
import java.util.TimerTask;

public class Demo9 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        //schedele里面有两个参数,一个是描述任务,一个时设置时间
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("my task");
            }
        },3000);

        System.out.println("main");
    }
}

 2.自己实现一个定时器

想要实现一个定时器,我们就必须要了解Timer内部都需要什么东西?

①描述一个任务

②组织任务

③执行时间到了的任务

2.1执行任务

创建一个专门的类来表示一个定时器中的任务(TimerTask)

【JavaEE学习日记】----多线程案例下_第1张图片

 2.2组织任务

使用一定的数据结构把一些任务放到一起,这里我们可以思考,安排任务的时候我们可以是无序的,例如先安排半小时之后干什么,然后在安排十分钟之后干什么,安排任务的时间是随机的,但是执行任务的顺序并不是随机的,肯定是时间小的先执行,不仅如此,此处的数据结构还要考虑线程安全问题,可能在多个线程里进行注册任务

在这里我们使用 PriorityBlockingQueue 作为组织任务的数据结构

【JavaEE学习日记】----多线程案例下_第2张图片

 2.3执行时间到了的任务

需要有一个线程不停的去检查当前优先队列的队首元素,看看当前最靠前的任务是不是时间到了 

【JavaEE学习日记】----多线程案例下_第3张图片

 执行结果:

【JavaEE学习日记】----多线程案例下_第4张图片

 我们可以发现报错,其实上述代码有两个比较严重的问题

1)MyTask并没有去设置比较规则

2)忙等问题,在检查队首元素的时候,由于是while(true),就会导致这个循环执行的非常快,如果队列不为空,并且时间也没有到,此时CPU既没有实质性的工作产出也没有进行休息,非常浪费CPU

完整代码:

import java.util.concurrent.PriorityBlockingQueue;

//实现一个计时器
class MyTask implements Comparable{
    private Runnable runnable;
    private long time;
    public MyTask(Runnable runnable,long delay){
        this.runnable = runnable;
        this.time = System.currentTimeMillis()+delay;
    }

    public long getTime(){
        return time;
    }

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

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

    }
}

class MyTimer{
    private PriorityBlockingQueue queue = new PriorityBlockingQueue<>();

    Object locker = new Object();

    public void schedule(Runnable runnable , long delay){
        MyTask task = new MyTask(runnable,delay);
        queue.put(task);
        synchronized (locker){
            locker.notify();
        }
    }

    public MyTimer(){
        Thread thread = new Thread(() -> {
            while(true){
                try {
                    MyTask task = queue.take();
                    long currTime = System.currentTimeMillis();
                    if(currTime < task.getTime()){
                        queue.put(task);
                        //解决忙等问题
                        synchronized (locker){
                            locker.wait(task.getTime() - currTime);
                        }
                    }else{
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }
}


public class Demo10 {
    public static void main(String[] args) {
        MyTimer timer = new MyTimer();
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        },3000);
        System.out.println("main");
    }
}

3.线程池

将线程提前创建好放进池子里,后面需要用到线程直接从池子里拿就可以了,线程用完之后也不需要还给系统,直接放回池子里就好了

为什么线程放在池子里会比从系统申请线程快呢?

这里就牵扯到内核态和用户态,对于我们日常写的代码就是用户态运行的,而有些代码需要调用操作系统的API,进一步的逻辑就会在内核中执行,所以在内核运行的代码我们就称为是内核态

为什么用户态会更快呢?举个例子,去餐厅吃饭,突然杯子没有水了,这时候有两个选择,一是自己去倒水(相当于用户态),二是呼叫服务员(相当于内核态),如果是自己去倒水目标就很明确,而呼叫服务员时,目标可能就不那么明确,有可能在中途被其他人叫走了,这样就造成了很多不确定因素

通过这个例子我们也就知道为什么用户态会比内核态速度更快。创建线程本身就需要内核的支持,调用start(),其实归根结底也是要进入内核态来运行,而把创建好的线程放到池子里,由于池子就是通过用户掏来实现的,这个过程就不涉及到内核

java标准库的线程池

ThreadPoolExecutor

 参数非常的多,但是最主要的就是前两个参数,可以将线程池比作是一个工厂,里面的线程比作是员工

1.int corePoolSize:核心线程数(相当于工厂里的正式员工)

2.int maximumPoolSize:做大线程数(相当于正式员工加上临时员工)

3.long keepAliveTime:(允许临时工摸鱼的时间)

4. TimeUnit unit,:时间单位

5.BlockingQueue workQueue:任务队列,线程池会提供一个submit方法,让我们把任务注册到线程池当中,并且加入到这个队列当中

6.ThreadFactory threadFactory:线程工厂,线程是怎么被创建出来的

7. RejectedExecutionHandler handler:拒绝策略,当队列任务满了怎么办,怎么解决

java标准库中简化版的线程池

Executors,本质是对ThreadPoolExecutor的一种封装,提供一些参数

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

//java系统的简化版线程池
public class Demo11 {
    public static void main(String[] args) {
        //创建一个固定数目的线程池
        ExecutorService poor = Executors.newFixedThreadPool(10);
        //创建一个自动扩容的线程池
        Executors.newCachedThreadPool();
        //创建一个只有一个线程的线程池
        Executors.newSingleThreadExecutor();
        //创建一个带有定时器功能的线程池
        Executors.newScheduledThreadPool();

        //可以在线程池里创建很多个任务,150个任务被十个线程分配
        for (int i = 0; i < 150; i++) {
            //通过runnable来指定任务
            poor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hallo thread");
                }
            });
        }

    }
}

4.自己实现一个线程池

实现线程池的几个步骤;

1.需要描述任务(可以使用Runnable)

2.需要能够组织任务(使用数据结构)

3.能够描述工作线程

4.需要组织这些线程

5.需要实现往线程池里添加任务

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

//自己实现一个线程池
class MyThreadPool{
    //描述和组织任务
    private BlockingQueue queue = new LinkedBlockingQueue<>();
    //描述一个线程,工作线程就是的功能就是任务队列中取出任务并执行
    static class Worker extends Thread{
        private BlockingQueue queue = null;
        //
        public Worker(BlockingQueue queue){
            this.queue = queue;
        }
        @Override
        public void run() {
            while(true){
                //循环的去读取线程中的任务
                try {
                    Runnable runnable = queue.take();
                    runnable.run();

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    private List workers = new ArrayList<>();
    //创建一个构造方法,创建出若干个线程,放到数组当中
    public MyThreadPool(int n){
        for (int i = 0; i < n; i++) {
            Worker worker = new Worker(queue);
            worker.start();
            workers.add(worker);
        }
    }

    //创建一个方法允许我们放任务到线程池当中
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
}

public class Demo12 {
    static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool = new MyThreadPool(10);
        for (int i = 0; i < 100; i++) {
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello word" + count);
                    count++;
                }
            });
        }

    }
}

你可能感兴趣的:(javaEE,java,javaee)