线程的创建

111 阅读3分钟

进程和线程的区别

  • 进程
  • 线程

继承Thread类创建


public class TestThread extends Thread {

    @Override
    public void run() {
        System.out.println("Thread");
    }

    public static void main(String[] args) {
        new TestThread().start();
    }

}

实现Runable接口创建


public class TestRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("Runnable");
    }

    public static void main(String[] args) {
        Thread thread = new Thread(new TestRunnable());
        thread.start();
    }

}
  • 由于JAVA是单继承多实现的特性,线程类实现Runnable后还可以继承其他类或者实现其他接口,使用方式更灵活

实现Callable接口创建


public class TestCallable implements Callable<String> {

    public static void main(String[] args) {
        FutureTask<String> futureTask = new FutureTask<String>(new TestCallable());
        new Thread(futureTask).start();
        try {
            System.out.println(futureTask.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String call() throws Exception {
        return "Callable";
    }
    
}
  • 除了接口的特性,提供了线程的返回值和异常抛出,可以调用futureTask.get()方法去获取线程异步的执行结果,但是,get方法在线程在执行完之前是阻塞的

通过线程池创建

  • 创建固定⼤⼩的线程池
    • Executors.newFixedThreadPool
  • 创建可缓存的线程池
    • Executors.newCachedThreadPool
  • 创建单个线程数的线程池
    • Executors.newSingleThreadExecutor
  • 创建可以执⾏延迟任务的线程池
    • Executors.newScheduledThreadPool
  • FixedThreadPool 和 SingleThreadPool,允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM
  • CachedThreadPool 和 ScheduledThreadPool,允许的创建的最大线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM
  • 阿里开发手册提倡使用自定义的线程池
    • 使用构造方法创建ThreadPoolExecutor
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
    
    • corePoolSize 线程池中的常驻核心线程数
    • maximumPoolSize 线程池能够容纳同时执行的最大线程数,必须大于1
    • keepAliveTime 多余的空闲线程的存活时间
    • unit 时间单位
    • workQueue 任务队列,存放被提交但是未执行的任务
    • threadFactory 生成线程池中的线程的线程工厂,用于创建线程,一般用默认的即可
    • handler 拒绝策略,当工作线程大于等于最大线程池数,并且工作队列已满,新提交的任务使用拒绝策略处理
      • JDK默认的拒绝策略有四种:
        • AbortPolicy(默认):直接抛出 RejectedExecutionException 异常阻止系统正常运行
        • CallerRunsPolicy:"调用者运行"一种调节机制,该策略既不会拋弃任务,也不会拋出异常,而是将某些任务回退到调用
        • DiscardoldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务
        • DiscardPolicy:直接丢弃任务,不子任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案

线程池的运行原理

    1. 在创建了线程池后,等待提交过来的任务请求。
    1. 当调用 execute( 方法添加一个请求任务时,线程池会做如下判断:
    • 2.1 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;
    • 2.2 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列:
    • 2.3 如果这时候队列满了且正在运行的线程数量还小于maximumPoolsize,那么还是要创建非核心线程立刻运行这个任务;
    • 2.4 如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。
    1. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
    1. 当一个线程无事可做超过一定的时间(keepAlive Time)时,线程池会判断: 如果当前运行的线程数大于corePoolsize,那么这个线程就被停掉。 所以线程池的所有任务完成后它最终会收缩到 corePoolsize 的大小。

如有错误请指正,谢谢