JAVAEE---多线程

阻塞队列的实现

class myBlockingQueue {
    private static String[] str = null;
    private static int size = 0;
    private static int head = 0;
    private static int tile = 0;
    Object locker = new Object();
    public myBlockingQueue(int n) {
        str = new String[n];
    }
    public void put(String s) throws InterruptedException {
        synchronized (locker) {
            while(size >= str.length) {
                locker.wait();
            }
            str[tile] = s;
            tile++;
            if(tile >= str.length) {
                tile = 0;
            }
            size++;
            locker.notify();
        }

    }
    public String take() throws InterruptedException {
        String s = null;
        synchronized (locker) {
            while(size == 0) {
                locker.wait();
            }
            s = str[head];
            head++;
            if(head >= str.length) {
                head = 0;
            }
            size--;
            locker.notify();
        }
        return s;
    }
}

这是阻塞队列的基本实现。

实现定时器

在标准库中,也有定时器的写法JAVAEE---多线程_第1张图片

schedule方法中,第一个参数是到时间要执行的代码,第二个参数是时间。我们还是要自己实现一个类似功能的计时器。

//方法中包含要执行的代码和执行时间
class MySch implements Comparable{
    private Runnable runnable;
    long time;
    public MySch(Runnable runnable, long delay) {
        this.runnable = runnable;
        this.time = System.currentTimeMillis()+delay;
    }
    public void run() {
        this.runnable.run();
    }
    @Override
    public int compareTo(MySch o) {
        return (int)this.time-(int)o.time;
    }
}
class MyTimer {
    //一个优先级阻塞队列来存放任务,存放的类型为Rannable,long两个类型的schdule类
     private PriorityBlockingQueue queue= new PriorityBlockingQueue<>();
    //创建线程来扫描队列
    private Thread t = null;
    private Object locker = new Object();
    public  void schdule(Runnable runnable, long delay) {
        synchronized (locker) {
            MySch mySch = new MySch(runnable,delay);
            queue.add(mySch);
            locker.notify();
        }

    }
    public void cancel() {
        t.interrupt();

    }
    //构造方法创建线程来执行任务
    public MyTimer() {
        t = new Thread(() -> {
            while(!Thread.currentThread().isInterrupted()) {
                synchronized (locker) {
                    while(queue.isEmpty()) {
                        try {
                            locker.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    MySch mySch = queue.peek();
                    if(mySch.time <= System.currentTimeMillis()) {
                        mySch.run();
                        queue.poll();
                    } else {
                        try {
                            locker.wait(mySch.time-System.currentTimeMillis());
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }

            }
        });
        t.start();
    }
}
public class ee_16 {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schdule(new Runnable() {
            @Override
            public void run() {
                System.out.println("3000");
            }
        },3000);
    }
}

这是我们实现的一个简单的计时器功能。

这里我们用一个优先级队列来存放我们要执行的任务,在构造方法中有一个线程来执行队列中的代码,而队列里面存放的元素包含两个内容,执行的内容和执行的时间。

线程池

我们为了解决进程开销过大引入了轻量级进程,也就是线程,但是线程数目过多也会影响速度,所以我们又有两种解决方法

1.轻量级线程---纤程/协程

2.线程池

我们这里结束的就是第二种,第一种在Java21中引入的虚拟线程其实就是那个意思。

线程池就是我们在使用后没有销毁而是放到线程池中,使用在拿到即可。

那为什么从线程池中拿比申请块呢?

这是因为从线程池中拿是用户态代码,而创建时内核态代码,用户态代码更可控

标准库中的线程池

ThreadPoolExecutor

这是它构造方法带有的参数

corePoolSize:核心线程数,即线程池中最少要存放的线程个数

maximumPoolSize:最大线程数,即线程池中最大存放的线程个数

keepAliveTime:非必要线程最多空闲下来的时间。

unit:单位,和keepAliveTime一起。

workQueue:存放执行的任务

threadFactory:线程工厂,用来创建线程

handler:拒绝策略,当队列满了在添加任务时的处理方法

AbortPolicy:报异常,两个都不做

CallerRunsPolicy:添加的线程由调用方执行,自己继续之前线程

DiscardOldestPolicy:丢弃老的线程执行新的

DiscardPolicy:丢弃新的线程(和第二个区别于这个新的线程不干),执行旧的线程。

class MyThreadPoolExecutor {
    //顺序表存放线程
    List list = new ArrayList<>();
    //阻塞队列存放任务
    private BlockingQueue queue = new ArrayBlockingQueue<>(1000);
    //submit方法添加任务
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
    //构造方法启动多个线程来执行任务
    public MyThreadPoolExecutor(int n) {
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                while(true) {
                    try {
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            t.start();
            list.add(t);
        }
    }
}
public class ee_19 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPoolExecutor myThreadPoolExecutor = new MyThreadPoolExecutor(10);
        for (int i = 0; i < 1000; i++) {
            int n =i;
            myThreadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(n);
                }
            });
        }
    }
}

因为ThreadPoolExecutor这个类本身用起来比较复杂所以Java提供了一个类Exectors这个类是一个工厂类,就是对ThreadPoolExector进行封装。

JAVAEE---多线程_第2张图片

我们在创建线程池的时候用的关键字是ExectorService来创建。

JAVAEE---多线程_第3张图片

这里主要的方法有newFixedThreadPool(int n)这个方法代表创建的线程数目是固定的。newCachedThreadPool()这个方法代表创建的线程池线程数可以扩容。newSingleThreadPool()这个方法代表单一线程池,即线程池中只包含一个线程。newScheduledThreadPool()这个方法就是定时器类似物,也可以起到延迟执行的作用。

那么我们什么时候使用Exectors什么时候使用ThreadPoolExector???

我们在简单实用一下的时候可以使用Exectors,我们高度定制化就需要使用ThreadPoolExector了。

那我们在使用的时候要创建多少个线程数呢?

这个问题没有一个固定的答案,这取决于线程是I/O密集型还是CPU密集型。CPU密集型占用cpu,而I/O密集型大部分时间在等待。所以我们应该实际尝试来确定线程数。

你可能感兴趣的:(java-ee,java,前端)