zoukankan      html  css  js  c++  java
  • Java多线程开发系列-线程管理

    主题:

    • 线程的未捕获异常
    • 线程工厂
    • 线程暂停
    • 线程池

    线程的未捕获异常

    在线程异常的时候,多线程运行不能按照顺序执行过程中捕获异常的方式来处理异常,异常会被直接抛出到控制台(由于线程的本质,使得你不能捕获从线程中逃逸的异常。一旦异常逃逸出任务的run方法,它就会向外传播到控制台,除非你采用特殊的形式捕获这种异常。)

    如下例:

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ThreadException implements Runnable{
      @Override
      public void run() {
        throw new RuntimeException();
      }
      //现象:控制台打印出异常信息,并运行一段时间后才停止
      public static void main(String[] args){
        //就算把线程的执行语句放到try-catch块中也无济于事
        try{
          ExecutorService exec = Executors.newCachedThreadPool();
          exec.execute(new ThreadException());
        }catch(RuntimeException e){
          System.out.println("Exception has been handled!");
        }
      }
    }

    使用UncaughtExceptionHandler可以捕获异常,并且重启新的线程来完成redo工作,这个例子里是重新初始化:init();

    import io.github.viscent.mtia.util.Debug;
    import io.github.viscent.mtia.util.Tools;
    
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.BlockingQueue;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    public class ThreadMonitorDemo {
      volatile boolean inited = false;
      static int threadIndex = 0;
      final static Logger LOGGER = Logger.getAnonymousLogger();
      final BlockingQueue<String> channel = new ArrayBlockingQueue<String>(100);
    
      public static void main(String[] args) throws InterruptedException {
        ThreadMonitorDemo demo = new ThreadMonitorDemo();
        demo.init();
        for (int i = 0; i < 100; i++) {
          demo.service("test-" + i);
        }
    
        Thread.sleep(2000);
        System.exit(0);
      }
    
      public synchronized void init() {
        if (inited) {
          return;
        }
        Debug.info("init...");
        WokrerThread t = new WokrerThread();
        t.setName("Worker0-" + threadIndex++);
        // 为线程t关联一个UncaughtExceptionHandler
        t.setUncaughtExceptionHandler(new ThreadMonitor());
        t.start();
        inited = true;
      }
    
      public void service(String message) throws InterruptedException {
        channel.put(message);
      }
    
      private class ThreadMonitor implements Thread.UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
          Debug.info("Current thread is `t`:%s, it is still alive:%s",
              Thread.currentThread() == t, t.isAlive());
    
          // 将线程异常终止的相关信息记录到日志中
          String threadInfo = t.getName();
          LOGGER.log(Level.SEVERE, threadInfo + " terminated:", e);
    
          // 创建并启动替代线程
          LOGGER.info("About to restart " + threadInfo);
          // 重置线程启动标记
          inited = false;
          init();
        }
    
      }// 类ThreadMonitor定义结束
    
      private class WokrerThread extends Thread {
        @Override
        public void run() {
          Debug.info("Do something important...");
          String msg;
          try {
            for (;;) {
              msg = channel.take();
              process(msg);
            }
          } catch (InterruptedException e) {
            // 什么也不做
          }
        }
    
        private void process(String message) {
          Debug.info(message);
          // 模拟随机性异常
          int i = (int) (Math.random() * 100);
          if (i < 2) {
            throw new RuntimeException("test");
          }
          Tools.randomPause(100);
        }
      }// 类ThreadMonitorDemo定义结束
    }

    线程工厂

    工厂设计模式是java中最常用的设计模式之一。它是一种创造性的模式,可用于开发一个或多个类需要的对象。有了这个工厂,我们可以集中创建对象。
    创造逻辑的集中给我们带来了一些优势,例如:
    很容易更改所创建对象的类或创建这些对象的方式。
    很容易为有限的资源限制对象的创建。例如,我们只能有N个类型的对象。
    很容易生成关于对象创建的统计数据。

    线程工厂实际上是一个接口:

    public interface ThreadFactory {
      Thread newThread(Runnable r);
    }

    实际上就是使用工厂模式在创建线程的时候统一操作。
    ThreadPoolExecutor具有ThreadFactory作为参数的构造函数。执行器创建新线程时使用此工厂。
    使用ThreadFactory,可以自定义由executor创建的线程,以便它们具有正确的线程名、优先级、设置为daemon等。

    import java.util.ArrayList;
    import java.util.Date;
    import java.util.Iterator;
    import java.util.List;
    import java.util.concurrent.ThreadFactory;
    import java.util.concurrent.TimeUnit;
    
    public class CustomThreadFactory implements ThreadFactory
    {
       private int          counter;
       private String       name;
       private List<String> stats;
    
       public CustomThreadFactory(String name)
       {
          counter = 1;
          this.name = name;
          stats = new ArrayList<String>();
       }
    
       @Override
       public Thread newThread(Runnable runnable)
       {
          Thread t = new Thread(runnable, name + "-Thread_" + counter);
          t.setDaemon(true);//守護線程
          t.setPriority(Thread.MAX_PRIORITY );//統一設置優先級
          counter++;
          stats.add(String.format("Created thread %d with name %s on %s 
    ", t.getId(), t.getName(), new Date()));
          return t;
       }
    
       public String getStats()
       {
          StringBuffer buffer = new StringBuffer();
          Iterator<String> it = stats.iterator();
          while (it.hasNext())
          {
             buffer.append(it.next());
          }
          return buffer.toString();
       }
       
       public static void main(String[] args)
       {
         CustomThreadFactory factory = new CustomThreadFactory("CustomThreadFactory");
         Task task = new Task();
         Thread thread;
         System.out.printf("Starting the Threads
    
    ");
         for (int i = 1; i <= 10; i++)
         {
          thread = factory.newThread(task);
          thread.start();
         }
         System.out.printf("All Threads are created now
    
    ");
         System.out.printf("Give me CustomThreadFactory stats:
    
    " + factory.getStats());
       }
    }
    
    class Task implements Runnable
    {
       @Override
       public void run()
       {
          try
          {
             TimeUnit.SECONDS.sleep(2);
          } catch (InterruptedException e)
          {
             e.printStackTrace();
          }
       }
    }

    线程暂停

    有3种暂停的方式

    • 1、自己进入睡眠暂停;
    • 2、使用CyclicBarrier,在某个阶段,多个线程统一暂停(await()),这里有介绍https://www.cnblogs.com/starcrm/p/12469364.html
    • 3、使用Condition信号暂停;

    第一种情况的例子:

    try {
      Thread.sleep(2000);
    } catch (InterruptedException ex) {
    }

    第三种情况的例子,通过主线程进行控制:

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class PauseControl extends ReentrantLock {
      private static final long serialVersionUID = 176912639934052187L;
      // 线程暂挂标志
      private volatile boolean suspended = false;
      private final Condition condSuspended = newCondition();
    
      /**
       * 暂停线程
       */
      public void requestPause() {
        suspended = true;
      }
    
      /**
       * 恢复线程
       */
      public void proceed() {
        lock();
        try {
          suspended = false;
          condSuspended.signalAll();
        } finally {
          unlock();
        }
      }
    
      /**
       * 当前线程仅在线程暂挂标记不为true的情况下才执行指定的目标动作。
       *
       * @targetAction 目标动作
       * @throws InterruptedException
       */
      public void pauseIfNeccessary(Runnable targetAction) throws InterruptedException {
        lock();
        try {
          while (suspended) {
            condSuspended.await();
          }
          targetAction.run();
        } finally {
          unlock();
        }
      }
    }
    
    import java.util.Scanner;
    
    public class ThreadPauseDemo {
      final static PauseControl pc = new PauseControl();
    
      public static void main(String[] args) {
        final Runnable action = new Runnable() {
          @Override
          public void run() {
            Debug.info("Master,I'm working...");
            Tools.randomPause(300);
          }
        };
        Thread slave = new Thread() {
          @Override
          public void run() {
            try {
              for (;;) {
                pc.pauseIfNeccessary(action);
              }
            } catch (InterruptedException e) {
              // 什么也不做
            }
          }
        };
        slave.setDaemon(true);
        slave.start();
        askOnBehaveOfSlave();
      }
    
      static void askOnBehaveOfSlave() {
        String answer;
        int minPause = 2000;
        try (Scanner sc = new Scanner(System.in)) {
          for (;;) {
            Tools.randomPause(8000, minPause);
            pc.requestPause();
            Debug.info("Master,may I take a rest now?%n");
            Debug.info("%n(1) OK,you may take a rest%n"
                + "(2) No, Keep working!%nPress any other key to quit:%n");
            answer = sc.next();
            if ("1".equals(answer)) {
              pc.requestPause();
              Debug.info("Thank you,my master!");
              minPause = 8000;
            } else if ("2".equals(answer)) {
              Debug.info("Yes,my master!");
              pc.proceed();
              minPause = 2000;
            } else {
              break;
            }
          }// for结束
        }// try结束
        Debug.info("Game over!");
      }
    }

    线程池

    线程池是预初始化线程的集合。一般来说,集合的大小是固定的,但不是强制的。它有助于使用相同的线程执行N个任务。如果有比线程更多的任务,那么任务需要在队列结构(FIFO–先进先出)中等待。
    当任何线程完成其执行时,它都可以从队列中获取新任务并执行它。所有任务完成后,线程将保持活动状态,并等待线程池中的更多任务。
    线程池的意义在于:
    1、减少在创建和销毁线程上所花的时间以及系统资源的开销,提升任务执行性能。
    2、控制进程中线程数量的峰值,避免系统开销过大。
    通过线程池,可创建一定数量的线程,并由线程池管理。在需要执行任务时,直接使用其中的线程。任务执行完成后,线程保留,并可用于执行下一个任务。如果任务比线程多,则等待线程空闲。

    直接构建ThreadPoolExecutor

    我们可以使用以下构造函数实例化ThreadPoolExecutor,其中ThreadFactory和RejectedExecutionHandler是可选参数,默认值分别是Executors.defaultThreadFactory()和ThreadPoolExecutor.AbortPolicy

    ThreadPoolExecutor(int corePoolSize,
                       int maximumPoolSize, 
                       long keepAliveTime, 
                       TimeUnit unit, 
                       BlockingQueue<Runnable> workQueue, 
                       ThreadFactory threadFactory, 
                       RejectedExecutionHandler handler) 
    • int corePoolSize : 定义核心池大小。对于每个新请求,即使池中有空闲线程,也会根据核心池大小创建一个新线程
    • int maximumPoolSize : 当发出请求并且核心池大小中的所有线程都忙时,将创建一个新线程,直到它达到最大池大小。在最大池大小之后,所有新请求都将进入队列。
    • long keepAliveTime: 这是等待空闲线程死亡的时间。只有当线程计数大于核心池大小且小于或等于最大核心池大小时,空闲线程才会在keepAliveTime之后死亡。
    • TimeUnit unit: keepAliveTime参数的时间单位
    • BlockingQueue<Runnable> workQueue : BlockingQueue是一个队列,如果一个线程想要获取一个元素,并且队列是空的,那么线程将被阻塞,并等待元素在队列中可用。以同样的方式添加元素时,如果队列中没有空间,线程将被阻塞并等待获得可用空间。
    • ThreadFactory threadFactory : 这是可选参数。传递用户定义的ThreadFactory
    • RejectedExecutionHandler handler: 这是可选参数。在两种情况下,ThreadPoolExecutor.execute()可以拒绝新任务。
      •   1、执行器已关闭。
      •   2、超过工作队列容量和最大线程使用有限的界限,即它们是饱和时
    • ThreadPoolExecutor.AbortPolicy:它中止任务并始终抛出RejectedExecutionException。
    • ThreadPoolExecutor.CallerRunsPolicy:它自己(客户端)执行被拒绝的任务。
    • ThreadPoolExecutor.DiscardPolicy:删除任务。
    • ThreadPoolExecutor.DiscardOldestPolicy:当ThreadPoolExecutor.execute()由于工作队列的有限边界和最大限制而拒绝任务时,此策略只将任务放在工作队列的头。

    默认策略是ThreadPoolExecutor.AbortPolicy。

    例子

    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    public class ThreadPoolExecutorDemoOne {
        public static void main(final String[] args) throws Exception {
            final ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 100, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<Runnable>(), new ThreadPoolExecutor.CallerRunsPolicy());
            executor.execute(new BookReader("Ramayan"));
            executor.execute(new BookReader("Mahabharat"));
            executor.execute(new BookReader("Veda"));
            System.out.println("Old Max Pool Size:"+ executor.getMaximumPoolSize());
            executor.setMaximumPoolSize(4);
            System.out.println("New Max Pool Size:"+ executor.getMaximumPoolSize());        
            executor.shutdown();
        }
    } 
    
    class BookReader implements Runnable {
      private String bookName;
      public BookReader(String bookName) {
        this.bookName = bookName;
      }
      @Override
      public void run() {
        for(int i = 0; i<5; i++) {
          System.out.println("Reading book: "+ bookName);
          try {
    
            Thread.sleep(200);
    
        } catch (InterruptedException ex) {
            System.out.println("I'm interrupted");
        }
        }
      }
    } 

    使用Executors工厂方法创建ThreadPoolExecutor实例

    可以使用Executors类提供的静态工厂方法来获取ThreadPoolExecutor,而不是直接使用上述构造函数之一创建ThreadPoolExecutor的实例。

    • newCachedThreadPool()–创建一个线程池,该线程池根据需要创建新线程,提供无容量的阻塞队列 SynchronousQueue,因此任务提交之后,将会创建新的线程执行;线程空闲超过60s将会销毁 。
    • newCachedThreadPool(ThreadFactory ThreadFactory)–创建一个线程池,该线程池根据需要创建新线程,但在可用时将重用先前构造的线程,并在需要时使用提供的ThreadFactory创建新线程。
    • newFixedThreadPool(int nThreads)–创建一个线程池,该线程池重用在共享的无边界队列上操作的固定数量的线程。构造一个固定线程数目的线程池,配置的corePoolSize与maximumPoolSize大小相同,同时使用了一个无界LinkedBlockingQueue存放阻塞任务,因此多余的任务将存在再阻塞队列,不会由RejectedExecutionHandler处理
    • newFixedThreadPool(int nThreads,ThreadFactory ThreadFactory)–创建一个线程池,该线程池重用在共享的无边界队列上操作的固定数量的线程,在需要时使用提供的ThreadFactory创建新线程。
    • newSingleThreadExecutor()–创建一个执行器,该执行器使用一个工作线程并在无限队列上操作,由一个线程串行执行任务。
    • newSingleThreadExecutor(ThreadFactory ThreadFactory)–创建一个执行器,该执行器使用一个在无限队列上操作的工作线程,并在需要时使用提供的ThreadFactory创建一个新线程。

    Executors的例子

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ExecutorExp {
      
      public static void main(String[] args) {
          ExecutorService executor = Executors.newSingleThreadExecutor();
          for(int i = 0; i < 4; i++) {
              executor.execute(new VoidTask());    
          }
          executor.shutdown();
      }
    
    }
    class VoidTask implements Runnable{
      @Override
      public void run() {
          System.out.println("Executing task (thread name)- " + Thread.currentThread().getName());
          // delay to keep the thread busy
          // so that pool is used
          try {
              Thread.sleep(500);
          } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
      }
    }

    newFixedThreadPool.

    此方法创建一个线程池,该线程池重用在共享的无界队列上操作的固定数量的线程。使用此方法时,内部Executors类使用以下参数创建ThreadPoolExecutor实例:

    new ThreadPoolExecutor(nThreads, nThreads,
              0L, TimeUnit.MILLISECONDS,
              new LinkedBlockingQueue());

    实例

    public class ExecutorExp {
      
      public static void main(String[] args) {
          ExecutorService executor = Executors.newFixedThreadPool(2);
          for(int i = 0; i < 4; i++) {
              executor.execute(new VoidTask());    
          }
          executor.shutdown();
      }
    
    }
    class VoidTask implements Runnable{
      @Override
      public void run() {
          System.out.println("Executing task (thread name)- " + Thread.currentThread().getName());
          // delay to keep the thread busy
          // so that pool is used
          try {
              Thread.sleep(500);
          } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
      }
    }

    newCachedThreadPool

    该线程池根据需要创建新线程,但在以前构造的线程可用时将重用它们。使用此方法时,内部Executors类使用以下参数创建ThreadPoolExecutor实例

    new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                           60L, TimeUnit.SECONDS,
                           new SynchronousQueue<Runnable>());

    实例

    public class ExecutorExp {
      
      public static void main(String[] args) {
          ExecutorService executor = Executors.newCachedThreadPool();
          for(int i = 0; i < 40; i++) {
              executor.execute(new VoidTask());    
          }
          executor.shutdown();
      }
    
    }
    class VoidTask implements Runnable{
      @Override
      public void run() {
          System.out.println("Executing task (thread name)- " + Thread.currentThread().getName());
          // delay to keep the thread busy
          // so that pool is used
          try {
              Thread.sleep(500);
          } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
      }
    }
    
    //为了验证最大线程数Integer.MAX_VALUE,我们看一下结果,感觉这个
    Executing task (thread name)- pool-1-thread-3
    Executing task (thread name)- pool-1-thread-4
    Executing task (thread name)- pool-1-thread-1
    Executing task (thread name)- pool-1-thread-2
    Executing task (thread name)- pool-1-thread-7
    Executing task (thread name)- pool-1-thread-8
    Executing task (thread name)- pool-1-thread-5
    Executing task (thread name)- pool-1-thread-6
    Executing task (thread name)- pool-1-thread-11
    Executing task (thread name)- pool-1-thread-10
    Executing task (thread name)- pool-1-thread-9
    Executing task (thread name)- pool-1-thread-12
    Executing task (thread name)- pool-1-thread-15
    Executing task (thread name)- pool-1-thread-13
    Executing task (thread name)- pool-1-thread-14
    Executing task (thread name)- pool-1-thread-16
    Executing task (thread name)- pool-1-thread-17
    Executing task (thread name)- pool-1-thread-18
    Executing task (thread name)- pool-1-thread-19
    Executing task (thread name)- pool-1-thread-20
    Executing task (thread name)- pool-1-thread-21
    Executing task (thread name)- pool-1-thread-23
    Executing task (thread name)- pool-1-thread-22
    Executing task (thread name)- pool-1-thread-24
    Executing task (thread name)- pool-1-thread-25
    Executing task (thread name)- pool-1-thread-26
    Executing task (thread name)- pool-1-thread-27
    Executing task (thread name)- pool-1-thread-28
    Executing task (thread name)- pool-1-thread-29
    Executing task (thread name)- pool-1-thread-30
    Executing task (thread name)- pool-1-thread-31
    Executing task (thread name)- pool-1-thread-32
    Executing task (thread name)- pool-1-thread-33
    Executing task (thread name)- pool-1-thread-34
    Executing task (thread name)- pool-1-thread-35
    Executing task (thread name)- pool-1-thread-36
    Executing task (thread name)- pool-1-thread-37
    Executing task (thread name)- pool-1-thread-39
    Executing task (thread name)- pool-1-thread-38
    Executing task (thread name)- pool-1-thread-40
    View Code

    构造有定时功能的线程池

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
            return new ScheduledThreadPoolExecutor(corePoolSize);
        }
    
    public static ScheduledExecutorService newScheduledThreadPool(
                int corePoolSize, ThreadFactory threadFactory) {
            return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
        }
    
    public ScheduledThreadPoolExecutor(int corePoolSize,
                                 ThreadFactory threadFactory) {
            super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
                  new DelayedWorkQueue(), threadFactory);
        }

    实例

    import java.util.Date;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
     
    public class ScheduledThreadPoolExecutorExample 
    {
        public static void main(String[] args) 
        {
            ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(2);
             
            DelayTask task = new DelayTask("Repeat Task");
            System.out.println("Created : " + task.getName());
             
            executor.scheduleWithFixedDelay(task, 2, 2, TimeUnit.SECONDS);
        }
    }
     
    class DelayTask implements Runnable {
        private String name;
     
        public DelayTask(String name) {
            this.name = name;
        }
     
        public String getName() {
            return name;
        }
     
        public void run() {
            System.out.println("Executing : " + name + ", Current Seconds : " + new Date().getSeconds());
        }
    }

    Callable接口

    Callable接口与Runnable接口相似,都用于定义线程的可执行任务。Callable在JDK 1.5引入,与Runnable相比,有三个优势:
    1、可以在任务中抛出异常
    2、可以终止任务
    3、可以获取任务的返回值
    这时候就会出现一个问题:新旧两种定义线程任务的方式,怎么前后兼容呢?
    这时候就出现了FutureTask这个类

    public class FutureTask<V> implements RunnableFuture<V> {
        // 封装实现Callable接口的任务
        public FutureTask(Callable<V> callable) { //...}
        // 封装实现Runnable接口的任务,result是返回值,在任务完成后赋值(futureTask.get()会返回这个值)。
        public FutureTask(Runnable runnable, V result) {//...}
    }
    public interface RunnableFuture<V> extends Runnable, Future<V> {
        void run();
    }

    使用FutureTask包装Runnable

    import java.util.concurrent.Callable;
    
    public class MyRunnable implements Runnable {
        @Override
        public void run() {
            for(int i=0;i<3;i++){
                System.out.println("num="+(i+1));
            }
        }
    }
    import java.util.concurrent.FutureTask;
    
    public class Test {
        public static void main(String[] agrs){
            String str = "default value";
            FutureTask<String> task = new FutureTask<String>(new MyRunnable(),str);
            new Thread(task).start();
            System.out.println(getFutureValue(task));
            System.out.println("after task.get()!");
        }
    
        public static String getFutureValue(FutureTask<String> task){
            String str = "default value1";
            try{
                str = task.get();
            }catch (Exception e){
                System.out.println("task is canceled!");
            }
            return str;
        }
    }

    线程池管理线程任务

    通过ExecutorService,可以使用4个方法执行线程任务。其中3个执行Runnable任务:
    其中3个执行Runnable任务:

    • void execute(Runnable command);
    • < T > Future< T > submit(Runnable task, T result);
    • Future< ? > submit(Runnable task);

    1个执行Callable任务:

    • < T > Future< T > submit(Callable< T > task); //执行Callable任务

    只有通过submit方法执行的线程任务,才能获取到Future,才能通过Future管理线程。
    示例代码如下:

    import java.util.concurrent.Callable;
    
    public class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception{
            int num=0;
            for(int i=0;i<3;i++){
                num++;
                Thread.sleep(100);
                System.out.println("num="+num);
            }
            return String.valueOf(num);
        }
    }
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class Test {
        public static void main(String[] agrs){
            ExecutorService service = Executors.newFixedThreadPool(2);
            Future<String> future = service.submit(new MyCallable()); // 使用submit()执行Callable任务
            System.out.println("执行结果:"+getFutureValue(future));
            System.out.println("after task.get()!");
            service.shutdown();
        }
    
        public static String getFutureValue(Future<String> task){
            String str = "default value";
            try{
                str = task.get();
            }catch (Exception e){
                System.out.println("task is canceled!");
            }
            return str;
        }
    }

    Executor和ExecutorService的区别

    见图

    Executors 是工厂helper类,包含几个用于为您创建预配置线程池实例的方法。这些类是一个很好的线程池起点。如果不需要应用任何自定义微调,请使用它。
    Executor和ExecutorService接口用于在Java中处理不同的线程池实现。通常,您应该保持代码与线程池的实际实现分离,并在整个应用程序中使用这些接口。
    Executor接口有一个execute方法来提交Runnable实例以供执行。

  • 相关阅读:
    暴力枚举 --- 多方法求解
    图论 --- 骑士周游问题,DFS
    数论 --- 同余定理
    数论 --- 筛法求素数进一步优化
    求大素数
    codeforces --- Round #244 (Div. 2) B. Prison Transfer
    codeforces --- Round #244 (Div. 2) A. Police Recruits
    线段树 --- (区间维护+逆推)
    线段数 --- (单点更新、求逆序对)
    线段树 --- (单点更新、区间求和、模板题)
  • 原文地址:https://www.cnblogs.com/starcrm/p/12517675.html
Copyright © 2011-2022 走看看