Java 线程池

一、自定义线程池

1. 介绍

1.1 引入原因

1. 一个任务过来,一个线程去做。如果每次过来都创建新线程,性能低且比较耗费内存
2. 线程数多于cpu核心,线程切换,要保存原来线程的状态,运行现在的线程,势必会更加耗费资源
   线程数少于cpu核心,不能很好的利用多线程的性能
   
3. 充分利用已有线程,去处理原来的任务

1.2. 线程池组件

1. 消费者(线程池):                 保存一定数量线程来处理任务
2. 生产者:                        客户端源源不断产生的新任务
3. 阻塞队列(blocking queue):      平衡消费者和生产者之间任务处理的一个等待队列


- 生产任务速度较快,多余的任务要等
- 生产任务速度慢,那么线程池中存活的线程等

在这里插入图片描述

2. 自定义线程池

2.1. 阻塞队列

package com.nike.erick.d06;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/*T 传入的实际就是Runnable对象*/
public class BlockingQueue<T> {
    /*queue的最大容量*/
    private int capacity;
    /*具体存放任务的队列*/
    private Deque<T> blockQueue = new ArrayDeque();
    /*公平锁*/
    private ReentrantLock reentrantLock = new ReentrantLock(true);

    /*任务生产者的等待队列*/
    private Condition producerRoom = reentrantLock.newCondition();

    /*任务消费者的等待队列*/
    private Condition consumerRoom = reentrantLock.newCondition();

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    /*阻塞添加
     * 1. 先判断阻塞队列是否满,
     *    如果满,则等待,
     *    如果没满,则添加,并唤醒消费的等待队列*/
    public void addTask(T task) {
        reentrantLock.lock();
        try {
            /*while循环,避免虚假唤醒*/
            while (blockQueue.size() == capacity) {
                try {
                    System.out.println("等待添加任务到阻塞队列:" + task);
                    producerRoom.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("任务添加到阻塞队列:" + task);
            blockQueue.addLast(task);
            consumerRoom.signalAll();
        } finally {
            reentrantLock.unlock();
        }
    }

    /*带超时时间的阻塞添加*/
    public boolean addTask(T task, long timeout, TimeUnit timeUnit) {
        /*统一时间单位*/
        long nanos = timeUnit.toNanos(timeout);

        reentrantLock.lock();
        try {
            while (blockQueue.size() == capacity) {
                try {
                    if (nanos < 0) {
                        return false;
                    }
                    /*等待时间: 等待一定时间后,就会继续运行*/
                    nanos = producerRoom.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            /*添加成功*/
            System.out.println("添加任务到阻塞队列:" + task);
            blockQueue.add(task);
            consumerRoom.signalAll();
            return true;
        } finally {
            reentrantLock.unlock();
        }
    }

    /*一直等待
     * 1. 如果阻塞队列中有任务,则直接取,并唤醒添加消息的
     * 2. 如果阻塞队列中没有任务,则等*/
    public T getTask() {
        reentrantLock.lock();

        try {
            while (blockQueue.isEmpty()) {
                try {
                    consumerRoom.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T task = blockQueue.removeFirst();
            producerRoom.signalAll();
            return task;
        } finally {
            reentrantLock.unlock();
        }
    }

    /*超时等待*/
    public T getTaskWithinTime(long timeout, TimeUnit timeUnit) {
        /*统一时间单位*/
        long nanos = timeUnit.toNanos(timeout);
        reentrantLock.lock();
        try {
            while (blockQueue.isEmpty()) {
                try {
                    /*只等待一定时间,超时之后怎么处理?返回null结果*/
                    if (nanos < 0) {
                        return null;
                    }
                    nanos = consumerRoom.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            T task = blockQueue.removeFirst();
            producerRoom.signalAll();
            return task;

        } finally {
            reentrantLock.unlock();
        }
    }

    public synchronized int size() {
        return blockQueue.size();
    }
}

2. 线程池

package com.nike.erick.d06;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class ErickThreadPool {
    /*池子中的阻塞队列*/
    private BlockingQueue blockingQueue;
    private int blockingQueueCapacity;

    /*池子中最大线程数*/
    private int coreThreadSize;

    /*超时等待的时间,时间单位*/
    private long timeout;
    private TimeUnit timeUnit;

    private Set<Worker> pool = new HashSet<>();

    /*线程池参数*/
    public ErickThreadPool(int coreThreadSize, int blockingQueueCapacity) {
        this.coreThreadSize = coreThreadSize;
        this.blockingQueue = new BlockingQueue(blockingQueueCapacity);
    }

    public ErickThreadPool(int coreThreadSize, int blockingQueueCapacity, long timeout, TimeUnit timeUnit) {
        this(coreThreadSize, blockingQueueCapacity);
        this.timeout = timeout;
        this.timeUnit = timeUnit;
    }

    /**
     * 任务具体执行流程: 外界接口的任务(Thread) 来了
     * 1. 当前池子没满,则新建一个线程并加入到池子中
     * 2. 如果池子已经满了,当前任务进入到阻塞队列中等待
     */
    public synchronized void execute(Runnable task, RejectHandler rejectHandler) {
        if (pool.size() < coreThreadSize) {
            Worker worker = new Worker(task);
            System.out.println("创建新的线程来执行任务:" + task);
            pool.add(worker);
            worker.start();
        } else {
            /*策略模式*/
            rejectHandler.addTask(task, blockingQueue);
        }
    }

    /*线程池中具体来干活的核心线程*/
    class Worker extends Thread {
        private Runnable task;

        public Worker(Runnable task) {
            this.task = task;
        }

        /**
         * 具体执行任务的方法
         * 1. 先执行当前任务,执行完毕后,检查阻塞队列中是否有任务
         * 1.1 如果有,则去执行
         * 1.2 如果没有,则等待: 阻塞等待
         */
        @Override
        public void run() {
            /*阻塞获取,一直等待*/
            while (task != null || (task = (Runnable) blockingQueue.getTaskWithinTime(timeout, timeUnit)) != null) {
                try {
                    task.run();
                } catch (Exception e) {
                    System.out.println("执行任务出错");
                } finally {
                    task = null;
                }
            }
            /*任务执行完毕后,将该线程从池子中移除*/
            synchronized (pool) {
                System.out.println("线程销毁:" + this);
                pool.remove(this);
            }
        }
    }
}

/*添加任务时候的具体的策略*/
interface RejectHandler {
    void addTask(Runnable task, BlockingQueue blockingQueue);
}

3. 测试

package com.nike.erick.d06;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/*T 传入的实际就是Runnable对象*/
public class BlockingQueue<T> {
    /*queue的最大容量*/
    private int capacity;
    /*具体存放任务的队列*/
    private Deque<T> blockQueue = new ArrayDeque();
    /*公平锁*/
    private ReentrantLock reentrantLock = new ReentrantLock(true);

    /*任务生产者的等待队列*/
    private Condition producerRoom = reentrantLock.newCondition();

    /*任务消费者的等待队列*/
    private Condition consumerRoom = reentrantLock.newCondition();

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    /*阻塞添加
     * 1. 先判断阻塞队列是否满,
     *    如果满,则等待,
     *    如果没满,则添加,并唤醒消费的等待队列*/
    public void addTask(T task) {
        reentrantLock.lock();
        try {
            /*while循环,避免虚假唤醒*/
            while (blockQueue.size() == capacity) {
                try {
                    System.out.println("等待添加任务到阻塞队列:" + task);
                    producerRoom.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("任务添加到阻塞队列:" + task);
            blockQueue.addLast(task);
            consumerRoom.signalAll();
        } finally {
            reentrantLock.unlock();
        }
    }

    /*带超时时间的阻塞添加*/
    public boolean addTask(T task, long timeout, TimeUnit timeUnit) {
        /*统一时间单位*/
        long nanos = timeUnit.toNanos(timeout);

        reentrantLock.lock();
        try {
            while (blockQueue.size() == capacity) {
                try {
                    if (nanos < 0) {
                        return false;
                    }
                    /*等待时间: 等待一定时间后,就会继续运行*/
                    nanos = producerRoom.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            /*添加成功*/
            System.out.println("添加任务到阻塞队列:" + task);
            blockQueue.add(task);
            consumerRoom.signalAll();
            return true;
        } finally {
            reentrantLock.unlock();
        }
    }

    /*一直等待
     * 1. 如果阻塞队列中有任务,则直接取,并唤醒添加消息的
     * 2. 如果阻塞队列中没有任务,则等*/
    public T getTask() {
        reentrantLock.lock();

        try {
            while (blockQueue.isEmpty()) {
                try {
                    consumerRoom.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T task = blockQueue.removeFirst();
            producerRoom.signalAll();
            return task;
        } finally {
            reentrantLock.unlock();
        }
    }

    /*超时等待*/
    public T getTaskWithinTime(long timeout, TimeUnit timeUnit) {
        /*统一时间单位*/
        long nanos = timeUnit.toNanos(timeout);
        reentrantLock.lock();
        try {
            while (blockQueue.isEmpty()) {
                try {
                    /*只等待一定时间,超时之后怎么处理?返回null结果*/
                    if (nanos < 0) {
                        return null;
                    }
                    nanos = consumerRoom.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            T task = blockQueue.removeFirst();
            producerRoom.signalAll();
            return task;

        } finally {
            reentrantLock.unlock();
        }
    }

    public synchronized int size() {
        return blockQueue.size();
    }
}

4. 获取任务超时等待

  • 线程池中的线程,在执行完毕任务后,会一直尝试从阻塞队列中获取任务
  • 设置超时等待,超时后,关闭线程池中的线程

3. 拒绝策略

  • 生产者添加了多个任务,任务的数量超过了 线程池中线程数+阻塞队列中最大阻塞数
  • 主线程就会被阻塞,一直不能往下进行
  • 应该给主线程一个选择的机会,决定在这个时候,如何执行新任务的添加
  • 超时未添加到阻塞队列里面,该如何做决定?拒绝策略
# 对于添加不进阻塞队列的任务,这个任务是由调用方来发起的
策略模式
1. 一直等待
2. 超时等待,过时不加
3. 让调用者自己放弃添加
4. 让调用者抛出异常
5. 让调用者自己执行任务

二、JDK线程池

1. 类图

在这里插入图片描述

2. 线程状态

  • ThreadPoolExecutor 使用int的高3位来表示线程池状态,低29位表示线程数量

在这里插入图片描述

3. ThreadPoolExecutor

3.1 构造方法

int corePoolSize:                     核心线程数
int maximumPoolSize:                 最大线程数
long keepAliveTime:                  救急线程数执行任务完后存活时间
TimeUnit unit:                       救急线程数执行任务完后存活时间
BlockingQueue<Runnable> workQueue:   阻塞队列
ThreadFactory threadFactory:         线程生产工厂,为线程起名字
RejectedExecutionHandler handler:    拒绝策略 


 public ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue,
                           ThreadFactory threadFactory,
                           RejectedExecutionHandler handler)

3.2 核心线程和救急线程

1. 核心线程: 执行完任务后,会继续保留在线程池中

2. 救急线程:如果阻塞队列已满,并且没有空余的核心线程。那么会创建救急线程来执行任务
  2.1 任务执行完毕后,这个线程就会被销毁(临时工)
  2.2 必须是有界阻塞,如果是无界队列,则不需要创建救急线程

3. 拒绝策略: 有界队列,核心线程满负荷,阻塞队列已满,无空余救急线程,才会执行拒绝

3.3 JDK拒绝策略

  • 如果线程达到最大线程数,救急线程也满负荷,且有界队列也满了,JDK 提供了4种拒绝策略
AbortPolicy:           调用者抛出RejectedExecutionException,  默认策略
CallerRunsPolicy:      调用者运行任务
DiscardPolicy:         放弃本次任务
DiscardOldestPolicy:   放弃队列中最早的任务,本任务取而代之

在这里插入图片描述

4. Executors类工厂方法

  • 默认的构造方法来创建线程池,参数过多,JDK提供了工厂方法,来创建线程池

4.1 固定大小

  • 核心线程数 = 最大线程数,救急线程数为0
  • 阻塞队列:无界
# 应用场景
任务量已知,但是线程执行时间较长
执行任务后,线程并不会结束
public static ExecutorService newFixedThreadPool(int nThreads) {
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

4.2 带缓冲

  • 核心线程数为0, 最大线程数为Integer的无限大
  • 全部是救急线程,等待时间是60s,60s后就会消亡
  • SynchronousQueue: 容量为0,只能存放一个,进来一个,必须出去后,后续的才能进来
  • 整个线程池数会随着任务数目增长,1分钟后没有其他活动会消亡
# 应用场景
1. 时间较短的线程
2. 数量大,任务执行时间长,会造成  OutOfMmeory问题
 public static ExecutorService newCachedThreadPool() {
     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                   60L, TimeUnit.SECONDS,
                                   new SynchronousQueue<Runnable>());
 }

4.3. 单线程

  • 线程池大小始终为1个,不能改变线程数
  • 相比自定义一个线程来执行,线程池可以保证前面任务的失败,不会影响到后续任务
# 1. 和自定义线程的区别
自定义线程:  执行多个任务时,一个出错,后续都能不能执行了
单线程池:    一个任务失败后,会结束出错线程。重新new一个线程来执行下面的任务

# 2. 执行顺序
单线程池: 保证所有任务都是串行

# 3. 和newFixedThreadPool的区别
newFixedThreadPool:          可以修改线程大小
newSingleThreadExecutor:     不可以修改
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
package com.nike.erick.d07;

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

public class Demo01 {
    public static void main(String[] args) {
        method03();
    }

    private static void method01() {
        ExecutorService pool = Executors.newFixedThreadPool(2);
        /*pool-1-thread-1   pool-1-thread-2  pool-1-thread-1*/
        pool.execute(() -> System.out.println(Thread.currentThread().getName() + " working"));
        pool.execute(() -> System.out.println(Thread.currentThread().getName() + " working"));
        pool.execute(() -> System.out.println(Thread.currentThread().getName() + " working"));
    }

    private static void method02() {
        ExecutorService pool = Executors.newCachedThreadPool();
        /*pool-1-thread-1  pool-1-thread-2  pool-1-thread-3*/
        pool.execute(() -> System.out.println(Thread.currentThread().getName() + " working"));
        pool.execute(() -> System.out.println(Thread.currentThread().getName() + " working"));
        pool.execute(() -> System.out.println(Thread.currentThread().getName() + " working"));
    }

    private static void method03() {
        ExecutorService pool = Executors.newSingleThreadExecutor();
        /*第一个任务执行失败后,后续任务会继续执行*/
        pool.execute(() -> {
            int i = 1 / 0;
            System.out.println(Thread.currentThread().getName() + " running");
        });

        pool.execute(() -> {
            System.out.println(Thread.currentThread().getName() + " running");
        });

        pool.execute(() -> {
            System.out.println(Thread.currentThread().getName() + " running");
        });
    }
}

5. 提交任务

5.1. execute

void execute(Runnable command)

5.2. submit

Future<?> submit(Runnable task);

// 可以从 Future 对象中获取一些执行任务的最终结果
<T> Future<T> submit(Runnable task, T result);

<T> Future<T> submit(Callable<T> task);

3. invokeAll

  • 执行集合中的所有的任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
    throws InterruptedException;

4. invokeAny

  • 集合中之要有一个任务执行完毕,其他任务就不再执行
package com.nike.erick.d07;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class Demo02 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService pool = Executors.newFixedThreadPool(10);
        method05(pool);
    }

    /* void execute(Runnable command) */
    public static void method01(ExecutorService pool) {
        pool.execute(() -> System.out.println(Thread.currentThread().getName() + " running"));
    }

    /*  <T> Future<T> submit(Runnable task, T result)
     * Future<?> submit(Runnable task) */
    public static void method02(ExecutorService pool) throws InterruptedException {
        Future<?> result = pool.submit(new Thread(() -> System.out.println(Thread.currentThread().getName() + " running")));
        TimeUnit.SECONDS.sleep(1);
        System.out.println(result.isDone());
        System.out.println(result.isCancelled());
    }

    /*
     * <T> Future<T> submit(Callable<T> task)*/
    public static void method03(ExecutorService pool) throws InterruptedException, ExecutionException {
        Future<String> submit = pool.submit(() -> "success");
        TimeUnit.SECONDS.sleep(1);
        System.out.println(submit.isDone());
        System.out.println(submit.isCancelled());
        System.out.println(submit.get()); // 返回结果是success
    }

    /* <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;*/
    public static void method04(ExecutorService pool) throws InterruptedException {
        Collection tasks = new ArrayList();
        for (int i = 0; i < 10; i++) {
            int round = i;
            tasks.add((Callable) () -> {
                System.out.println(Thread.currentThread().getName() + " running");
                return "success:" + round;
            });
        }
        List results = pool.invokeAll(tasks);

        TimeUnit.SECONDS.sleep(1);
        System.out.println(results);
    }

    /*
     *     <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
     * */
    public static void method05(ExecutorService pool) throws InterruptedException, ExecutionException {
        ExecutorService service = Executors.newFixedThreadPool(1);
        Collection<Callable<String>> tasks = new ArrayList<>();

        tasks.add(() -> {
            System.out.println("first task");
            TimeUnit.SECONDS.sleep(1);
            return "success";
        });

        tasks.add(() -> {
            System.out.println("second task");
            TimeUnit.SECONDS.sleep(2);
            return "success";
        });


        tasks.add(() -> {
            System.out.println("third task");
            TimeUnit.SECONDS.sleep(3);
            return "success";
        });
        // 任何一个任务执行完后,就会返回结果
        String result = pool.invokeAny(tasks);
        System.out.println(result);
    }
}

6. 关闭线程池

6.1 shutdown

  • 将线程池的状态改变为SHUTDOWN状态
  • 不会接受新任务,已经提交的任务不会停止
  • 不会阻塞调用线程的执行
void shutdown();
package com.dreamer.multithread.day09;

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

public class Demo04 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + " first running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        pool.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + " second running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        pool.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + " third running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 不会阻塞主线程的执行
        pool.shutdown();
        System.out.println("main thread ending");

    }
}

6.2. shutdownNow

  • 不会接受新任务
  • 没执行的任务会打断
  • 将等待队列中的任务返回
List<Runnable> shutdownNow();
package com.dreamer.multithread.day09;

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

public class Demo04 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + " first running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        pool.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + " second running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        pool.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + " third running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 不会阻塞主线程的执行
        List<Runnable> leftOver = pool.shutdownNow();
        System.out.println(leftOver.size()); // 2
        System.out.println("main thread ending"); 
    }
}

三、线程池拓展

1. 线程数量

  • 过小,导致cpu资源不能充分利用,浪费性能
  • 过大,线程上下文切换,导致占用内存过多,浪费性能

1.1 CPU密集型

  • 如果线程的任务主要是和cpu资源打交道,称为CPU密集型
  • 线程数量: 核心数+1
  • +1: 保证某线程由于某些原因(操作系统方面)导致暂停时,额外线程可以启动,不浪费CPU资源

1. 2. IO密集型

  • IO操作,RPC调用,数据库访问时,CPU是空闲的,称为IO密集型
  • 线程数 = 核数* 期望cpu利用率* (cpu计算时间+cpu等待时间)/cpu计算时间

在这里插入图片描述

2. 调度功能

2.1. 延时执行

  • 如果希望线程延时执行任务
package com.dreamer.multithread.day09;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo05 {
    public static void main(String[] args) {
        // 两个线程,分别可以延时执行不同的任务
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);

        // 延时1s后执行
        pool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " first running");
            }
        }, 1, TimeUnit.SECONDS);

        // 延时5s后执行
        pool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " second running");
            }
        }, 5, TimeUnit.SECONDS);
    }
}
package com.dreamer.multithread.day09;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo06 {
    public static void main(String[] args) {
        // 如果是单个线程,则延时的任务是串行执行的
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);

        // 如果一个线程出错,则会再次创建一个线程来执行任务
        pool.schedule(new Runnable() {
            @Override
            public void run() {
                int i = 1 / 0;
                System.out.println(Thread.currentThread().getName() + " first running");
            }
        }, 1, TimeUnit.SECONDS);

        pool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " second running");
            }
        }, 2, TimeUnit.SECONDS);
    }
}

2. 定时执行

  • scheduleAtFixedRate
  • scheduleWithFixedDelay
package com.dreamer.multithread.day09;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo07 {
    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);

        // 定时执行任务
        pool.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("task is running");
            }
        }, 3, 2, TimeUnit.SECONDS);
        //  初始延时,   任务间隔时间,    任务间隔时间单位
    }
}

3. 异常处理

3.1. 不处理异常

  • 任务执行过程中,业务中的异常并不会抛出
package com.dreamer.multithread.day09;

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

public class Demo08 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(new Runnable() {
            @Override
            public void run() {
                int i = 1 / 0;
                System.out.println(Thread.currentThread().getName() + " task running");
            }
        });
    }
}

2. 任务执行者自己处理

package com.dreamer.multithread.day09;

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

public class Demo08 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    int i = 1 / 0;
                    System.out.println(Thread.currentThread().getName() + " task running");
                } catch (Exception e) {
                    e.printStackTrace();
                    return;
                }
            }
        });
    }
}

3. 线程池处理

package com.dreamer.multithread.day09;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class Demo08 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        Future<?> result = pool.submit(new Runnable() {
            @Override
            public void run() {
                int i = 1 / 0;
                System.out.println(Thread.currentThread().getName() + " task running");
            }
        });

        TimeUnit.SECONDS.sleep(1);
        // 获取结果的时候,就可以把线程执行任务过程中的异常报出来
        System.out.println(result.get());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值