线程池使用

269 阅读4分钟

1、线程池理解

在程序中,创建一个线程,需要分配给该线程内存,还要进行系统调用,频繁的创建和销毁线程,会大大降低系统的运行效率,线程池的作用在于,线程可以不用频繁创建和销毁,可以一直复用,提升系统性能和和进行线程管理;

2、jdk提供几种线程池使用

(1)缓冲线程池,使用例子如下:

public class NewCachedThreadPoolDemo {

   public static void main(String[] args) {
       ExecutorService singleThreadExecutor = Executors.newCachedThreadPool();
       for (int i = 0; i < 7; i++) {
           final int a = i;
           singleThreadExecutor.execute(() -> {
               System.out.println("线程执行:" + a);
           });
       }
       // 线程池销毁
       singleThreadExecutor.shutdown();
   }
}

该线程池有个弊端,就是最大线程数默认是Integer.MAX_VALUE,即是0x7fffffff,线程可以默认无限扩大,采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程;

(2)固定线程池,使用例子如下:

public class NewFixedThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService singleThreadExecutor = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 7; i++) {
            final int a = i;
            singleThreadExecutor.execute(() -> {
                System.out.println("线程执行:" + a);
            });
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        // 线程池销毁
        singleThreadExecutor.shutdown();
    }
}

该线程池有个弊端,就是队列是使用了LinkedBlockingQueue,该队列可以无限堆积线程任务,导致系统任务越来越大;

(3)单个线程池,使用例子如下:

public class NewSingleThreadExecutorDemo {

    public static void main(String[] args) {
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        // 批量添加线程
        for (int i = 0; i < 5; i++) {
            final int a = i;
            singleThreadExecutor.execute(() -> {
                System.out.println("线程执行:" + a);
            });
        }
        singleThreadExecutor.shutdown();
    }
}

该线程池有个弊端,就是队列是使用了LinkedBlockingQueue,该队列可以无限堆积线程任务,导致系统任务越来越大; 备注:如果线程池异常的话,线程会重新创建一个线程,重新使用

(4)调度线程池,使用例子如下:

public class ScheduledThreadPoolDemo {

  public static void main(String[] args) {
    ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);
    System.out.println("添加任务的时间:" + LocalDateTime.now());
    ses.schedule(() -> System.out.println("执行子任务:" + LocalDateTime.now()), 3, TimeUnit.SECONDS);
  }
}

可以定时执行任务

3、线程池的执行原理

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue,
                      ThreadFactory threadFactory,
                      RejectedExecutionHandler handler);
线程池的几个参数:
corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:空闲线程存活时间
TimeUnit:时间单位
BlockingQueue:线程池任务队列
ThreadFactory:创建线程的工厂
RejectedExecutionHandler:拒绝策略

当任务来时,当线程池中的线程数量小于核心线程数时,就会新建一个线程执行该任务,当该线程数打到核心线程数时,就会把任务放到队列中,当队列满了,就会当线程数量小于创建最大线程数时,就会创建线程去执行任务,当队列满了,并且最大线程数超过了定义的数量,就会触发拒绝机制;

4、 线程池的拒绝策略

拒绝策略:当线程池的任务超出线程池队列可以存储的最大值之后,执行的策略。默认的拒绝策略有以下 4 种:

AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
CallerRunsPolicy:使用当前调用的线程来执行此任务。
DiscardOldestPolicy:丢弃队列头部(最旧)的一个任务,并重新执行当前任务(重复此过程)。
DiscardPolicy:也是丢弃任务,但是不抛出异常。

备注:RejectedExecutionHandler是一个接口类,也可以自己实现该类中的方法,自己定义拒绝策略,默认的拒绝策略是直接抛出异常;

5、 线程池关闭中方法区别

(1)shutdown():该关闭会等线程池中的任务执行完毕,再关闭线程池;

(2)shutdownNow():该关闭会立即关闭线程池,不等方法执行完毕;

6、 线程池提交任务:

(1)execute():该方法是一个Runnable类;

(2)submit():该方法可以是一个Runnable类,也可以是一个Callable类,Callable可以有返回值,可以使用 Future接收结果:

public class ThreadPool1Demo {

 public static void main(String[] args) {
   ExecutorService executorService = Executors.newSingleThreadExecutor();

   Future<String> future = executorService.submit(() -> {
     System.out.println(Thread.currentThread().getName() + ":哈哈");
     return "success";
   });
   try {
     String result = future.get();
     System.out.println("结果为:" + result);
   } catch (InterruptedException | ExecutionException e) {
     e.printStackTrace();
   }
   executorService.shutdown();
 }

}

备注:Future还可以捕获submit抛出的异常结果

public class ThreadPool1Demo {

public static void main(String[] args) {
 ExecutorService executorService = Executors.newSingleThreadExecutor();

 Future<String> future = executorService.submit(() -> {
   System.out.println(Thread.currentThread().getName() + ":哈哈");
   int a = 1 / 0;
   return "success";
 });
//    try {
//      String result = future.get();
//      System.out.println("结果为:" + result);
//    } catch (InterruptedException | ExecutionException e) {
//      e.printStackTrace();
//    }
 executorService.shutdown();
}

}

7、 自定义线程池

在系统开发中,一般不适用jdk默认的几种线程池,因为存在弊端,阿里巴巴开发手册也不推荐,所以一般使用自定义线程池:

public class ThreadPoolExecutor1Demo {

  public static void main(String[] args) {
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,
        0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
    threadPool.execute(()->{
      System.out.println(12);
    });
    threadPool.shutdown();
  }
}

8、线程工厂

线程工厂,即ThreadFactory,用来创建线程,新线程是使用ThreadFactory创建的,如果没有指定,是使用默认的线程工厂

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadFactoryDemo {

  public static void main(String[] args) {
    ThreadFactory threadFactory = new ThreadFactory() {
      AtomicInteger atomicInteger = new AtomicInteger(0);

      @Override
      public Thread newThread(Runnable r) {
        Thread thread = new Thread(r, "aa->" + atomicInteger.getAndIncrement());
        return thread;
      }
    };
    ExecutorService executorService = Executors.newFixedThreadPool(10, threadFactory);
    executorService.submit(() -> {
      System.out.println("线程池名称为:" + Thread.currentThread().getName());
      System.out.println("hello world");
    });
    executorService.shutdown();
  }
}

9、总结

总之,一般在使用线程池的使用,都是自定义线程池,而且线程池也是要合理的运用,才能提高系统效率,根据自己的开发系统代码定义;