精华内容
下载资源
问答
  • 系统日志有 RejectedExecutionException 异常抛异常线程池是业务程序为了某类处理过程比较慢的请求创建的,线程资源隔离,避免阻塞这个系统。异常信息类似下面输出: java.util.concurrent....

    背景介绍

    系统日志有 RejectedExecutionException 异常,抛异常的线程池是业务程序为了某类处理过程比较慢的请求创建的,线程资源隔离,避免阻塞这个系统。异常信息类似下面输出:

    java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@56235b8e rejected from 
    java.util.concurrent.ThreadPoolExecutor@49c2faae
    [Running, pool size = 80, active threads = 3, queued tasks = 3, completed tasks = 1674]
    

    发现 active threads 的值为3,队列是满的,线程池的大小是 80,已经达到最大线程数。由于对 active threads 值确切的含义还不是很清晰,因此产生了疑问:活跃线程数才 3 个就触发了拒绝策略,其他的 77 个线程在干嘛呢?为什么 active threads 不等于 pool size?

    源码分析

    在官方文档上有说明 getActiveCount() 方法获取的值是活跃线程的大致值,不是准确值。只看文档说明还是不能有清晰的认识,活跃线程代表的是什么意思,值又是怎么获取的。带着这样的疑问就去JDK源码中找答案。

    线程池对象调用 submit 方法提交任务后,会执行 ThreadPoolExecutor 类中的 execute 方法:

    public void execute(Runnable var1) {
            if (var1 == null) {
                throw new NullPointerException();
            } else {
                int var2 = this.ctl.get();
                if (workerCountOf(var2) < this.corePoolSize) {
                    if (this.addWorker(var1, true)) {
                        return;
                    }
    
                    var2 = this.ctl.get();
                }
    
                if (isRunning(var2) && this.workQueue.offer(var1)) {
                    int var3 = this.ctl.get();
                    if (!isRunning(var3) && this.remove(var1)) {
                        this.reject(var1);
                    } else if (workerCountOf(var3) == 0) {
                        this.addWorker((Runnable)null, false);
                    }
                } else if (!this.addWorker(var1, false)) {
                    this.reject(var1);
                }
    
            }
        }
    

    从日志输出情况来看,提交任务时队列已满,就会走最下面的 else if (!this.addWorker(var1, false))语句,由于线程池已经达到最大线程线程数,很明显 addWorker 方法会返回 false,从而执行拒绝逻辑抛出异常。

    异常信息是 ThreadPoolExecutor 类的 toString 方法获取的

    public String toString() {
            ReentrantLock var5 = this.mainLock;
            var5.lock();
    
            long var1;
            int var3;
            int var4;
            try {
                var1 = this.completedTaskCount;
                var4 = 0;
                var3 = this.workers.size();
                Iterator var6 = this.workers.iterator();
    
                while(var6.hasNext()) {
                    ThreadPoolExecutor.Worker var7 = (ThreadPoolExecutor.Worker)var6.next();
                    var1 += var7.completedTasks;
                    if (var7.isLocked()) {
                        ++var4;
                    }
                }
            } finally {
                var5.unlock();
            }
    
            int var11 = this.ctl.get();
            String var12 = runStateLessThan(var11, 0) ? "Running" : (runStateAtLeast(var11, 1610612736) ? "Terminated" : "Shutting down");
            return super.toString() + "[" + var12 + ", pool size = " + var3 + ", active threads = " + var4 + ", queued tasks = " + this.workQueue.size() + ", completed tasks = " + var1 + "]";
        }
    

    从代码中可以看出 pool size 的值是成员变量 this.workers 的元素数量大小,其类型是 HashSet。而 active threads 的值是 this.workers 中元素是 isLocked 状态的数量总和。isLocked 方法是判断元素(ThreadPoolExecutor.Worker 类型)的 state 变量是否为 0 ,ThreadPoolExecutor.Worker 继承了 AbstractQueuedSynchronizer 类,state 是 AQS 用于实现同步的一个状态变量。那在 ThreadPoolExecutor.Worker 中是代表什么含义呢?

    在 runWorker 方法中有对 state 变量状态进行改变, runWorker 方法主要逻辑就是线程不断从队列中获取任务然后执行任务。当线程获取到任务后就会改变 state 状态,获取锁 var1.lock();,以及执行完任务后释放锁 var1.unlock();

    final void runWorker(ThreadPoolExecutor.Worker var1) {
            Thread var2 = Thread.currentThread();
            Runnable var3 = var1.firstTask;
            var1.firstTask = null;
            var1.unlock();
            boolean var4 = true;
    
            try {
                while(var3 != null || (var3 = this.getTask()) != null) {
                    var1.lock();
                    if ((runStateAtLeast(this.ctl.get(), 536870912) || Thread.interrupted() && runStateAtLeast(this.ctl.get(), 536870912)) && !var2.isInterrupted()) {
                        var2.interrupt();
                    }
    
                    try {
                        this.beforeExecute(var2, var3);
                        Object var5 = null;
    
                        try {
                            var3.run();
                        } catch (RuntimeException var28) {
                            var5 = var28;
                            throw var28;
                        } catch (Error var29) {
                            var5 = var29;
                            throw var29;
                        } catch (Throwable var30) {
                            var5 = var30;
                            throw new Error(var30);
                        } finally {
                            this.afterExecute(var3, (Throwable)var5);
                        }
                    } finally {
                        var3 = null;
                        ++var1.completedTasks;
                        var1.unlock();
                    }
                }
    
                var4 = false;
            } finally {
                this.processWorkerExit(var1, var4);
            }
    
        }
    

    因此也就知道 active threads 表示的是正在执行任务中线程数。

    程序复现

    package org.yrs.jvm;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @Author: yangrusheng
     * @Description:
     * @Date: Created in 9:19 2020/12/2
     * @Modified By:
     */
    public class ThreadTest {
    
        public static void main(String[] args) {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 80, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), new ThreadPoolExecutor.AbortPolicy());
            for (int i=0; i< 500000; i++) {
                try {
                    threadPoolExecutor.submit(new Thread( () -> {
                        try {
                            Thread.sleep(1000L);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }));
                } catch (Exception e) {
                    System.out.println(e);
                }
                if (i % 100 == 0 ) {
                    try {
                        Thread.sleep(2000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    总结

    active thread 表示的是线程获取任务后,正在执行任务中的一个状态。在执行任务完成后会改变其状态为“not active thread”,然后会不停的去获取任务,直到下次成功获取新的任务,状态才又变为“active thread”。pool size 表示的是线程池中总的线程数,它们两个值之所以不相等或者说相差很大,是因为线程除了正在执行任务的状态,还有成功获取任务前的一段时间。

    展开全文
  • 线程池正常运行的情况下,如果忽然某条线程执行任务时,抛出异常,这个时候线程池会怎么处理呢? 准备测试代码(测试代码需要jdk1.8以上) public class ThreadThrowsExceptionTest { public static void main...

    线程池正常运行的情况下,如果忽然某条线程执行任务时,抛出了异常,这个时候线程池会怎么处理呢?
    准备测试代码(测试代码需要jdk1.8以上)

    public class ThreadThrowsExceptionTest {
        public static void main(String[] args) {
            ExecutorService executorService = new ThreadPoolExecutor(5, 5,
                    0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<Runnable>(), new DefaultThreadFactory(),
                    new ThreadPoolExecutor.AbortPolicy());
            executorService.submit(() -> {
                System.out.println("线程" + Thread.currentThread().getName() + "准备抛空指针异常了");
                throw new NullPointerException("线程抛出空指针啦");
            });
            IntStream.range(0, 4).forEach(index -> executorService.submit(() -> System.out.println("线程" + Thread.currentThread().getName() + "执行了任务" + index)));
    
        }
    
        //这里借用了Executors中DefaultThreadFactory,进行了简单修改,方便测试
        static class DefaultThreadFactory implements ThreadFactory {
            private static final AtomicInteger poolNumber = new AtomicInteger(1);
            private final ThreadGroup group;
            private final AtomicInteger threadNumber = new AtomicInteger(1);
            private final String namePrefix;
    
            DefaultThreadFactory() {
                SecurityManager s = System.getSecurityManager();
                group = (s != null) ? s.getThreadGroup() :
                        Thread.currentThread().getThreadGroup();
                namePrefix = "pool-" +
                        poolNumber.getAndIncrement() +
                        "-thread-";
            }
            public Thread newThread(Runnable r) {
                Thread t = new Thread(group, r,
                        namePrefix + threadNumber.getAndIncrement(),
                        0);
                System.out.println("创建线程了" + t.getName());
                if (t.isDaemon())
                    t.setDaemon(false);
                if (t.getPriority() != Thread.NORM_PRIORITY)
                    t.setPriority(Thread.NORM_PRIORITY);
                return t;
            }
        }
    
    }
    

    这里的DefaultThreadFactory借用了Executors中DefaultThreadFactory的源码,进行了简单修改,方便我们调试时查看创建的线程信息。例子中提交了4个正常任务,1个抛异常的任务。执行情况如下

    创建线程了pool-1-thread-1
    线程pool-1-thread-1准备抛空指针异常了
    创建线程了pool-1-thread-2
    创建线程了pool-1-thread-3
    创建线程了pool-1-thread-4
    线程pool-1-thread-2执行了任务0
    线程pool-1-thread-3执行了任务1
    创建线程了pool-1-thread-5
    线程pool-1-thread-4执行了任务2
    线程pool-1-thread-5执行了任务3
    

    线程池创建了5条工作线程,正常执行了5个任务,其中一个抛出异常也没有影响其他工作线程,似乎线程池没有进行任何处理。
    由于Future获取异常的时机是在调用get方法时,这里需要我们稍稍改下代码

    public class ThreadThrowsExceptionTest {
        public static void main(String[] args) {
            ExecutorService executorService = new ThreadPoolExecutor(5, 5,
                    60L, TimeUnit.MINUTES,
                    new LinkedBlockingQueue<Runnable>(), new DefaultThreadFactory(),
                    new ThreadPoolExecutor.AbortPolicy());
            //这里改为调用execute
            executorService.execute(() -> {
                System.out.println("线程" + Thread.currentThread().getName() + "准备抛空指针异常了");
                throw new NullPointerException("线程抛出空指针啦");
            });
            IntStream.range(0, 4).forEach(index -> executorService.submit(() -> System.out.println("线程" + Thread.currentThread().getName() + "执行了任务" + index)));
    
        }
    
        ...
    
    }
    

    执行结果如下

    创建线程了pool-1-thread-1
    线程pool-1-thread-1准备抛空指针异常了
    创建线程了pool-1-thread-2
    Exception in thread "pool-1-thread-1" java.lang.NullPointerException: 线程抛出空指针啦
    	at com.example.homework.thread.ThreadThrowsExceptionTest.lambda$main$0(ThreadThrowsExceptionTest.java:20)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    	at java.lang.Thread.run(Thread.java:745)
    创建线程了pool-1-thread-3
    创建线程了pool-1-thread-4
    创建线程了pool-1-thread-5
    线程pool-1-thread-4执行了任务1
    线程pool-1-thread-3执行了任务0
    创建线程了pool-1-thread-6
    线程pool-1-thread-5执行了任务2
    线程pool-1-thread-6执行了任务3
    

    这里出现了pool-1-thread-6!看来线程池在线程执行异常后新创建了一条线程。
    那么具体原因需要跟踪源码看一下

    public Future<?> submit(Runnable task) {
            if (task == null) throw new NullPointerException();
            RunnableFuture<Void> ftask = newTaskFor(task, null);
            //这里调用的是execute方法
            execute(ftask);
            return ftask;
        }
    

    由于submit方法中也是调用execute方法,所以我们只需要关注execute方法实现,execute方法细节如下

    public void execute(Runnable command) {
            if (command == null)
                throw new NullPointerException();
          	
            int c = ctl.get();
            if (workerCountOf(c) < corePoolSize) {
            	//添加线程的主要方法
                if (addWorker(command, true))
                    return;
                c = ctl.get();
            }
            if (isRunning(c) && workQueue.offer(command)) {
                int recheck = ctl.get();
                if (! isRunning(recheck) && remove(command))
                    reject(command);
                else if (workerCountOf(recheck) == 0)
                    addWorker(null, false);
            }
            else if (!addWorker(command, false))
                reject(command);
        }
    
    private boolean addWorker(Runnable firstTask, boolean core) {
            ...
            boolean workerStarted = false;
            boolean workerAdded = false;
            Worker w = null;
            try {
            	//这里通过Worker包装task
                w = new Worker(firstTask);
                final Thread t = w.thread;
                if (t != null) {
                    final ReentrantLock mainLock = this.mainLock;
                    mainLock.lock();
                    try {
                        // Recheck while holding lock.
                        // Back out on ThreadFactory failure or if
                        // shut down before lock acquired.
                        int rs = runStateOf(ctl.get());
    
                        if (rs < SHUTDOWN ||
                            (rs == SHUTDOWN && firstTask == null)) {
                            if (t.isAlive()) // precheck that t is startable
                                throw new IllegalThreadStateException();
                            workers.add(w);
                            int s = workers.size();
                            if (s > largestPoolSize)
                                largestPoolSize = s;
                            workerAdded = true;
                        }
                    } finally {
                        mainLock.unlock();
                    }
                    if (workerAdded) {
                    	//线程开始执行
                        t.start();
                        workerStarted = true;
                    }
                }
            } finally {
                if (! workerStarted)
                    addWorkerFailed(w);
            }
            return workerStarted;
        }
    

    这里没有对执行的情况做处理,那就只有继续跟踪Worker的代码

    private final class Worker
            extends AbstractQueuedSynchronizer
            implements Runnable
        {
            ...
            Worker(Runnable firstTask) {
                setState(-1); // inhibit interrupts until runWorker
                this.firstTask = firstTask;
                this.thread = getThreadFactory().newThread(this);
            }
    
            /** Delegates main run loop to outer runWorker  */
            public void run() {
            	//执行线程的关键方法
                runWorker(this);
            }
    
    final void runWorker(Worker w) {
            Thread wt = Thread.currentThread();
            Runnable task = w.firstTask;
            w.firstTask = null;
            w.unlock(); // allow interrupts
            boolean completedAbruptly = true;
            try {
                while (task != null || (task = getTask()) != null) {
                    w.lock();
                    // If pool is stopping, ensure thread is interrupted;
                    // if not, ensure thread is not interrupted.  This
                    // requires a recheck in second case to deal with
                    // shutdownNow race while clearing interrupt
                    if ((runStateAtLeast(ctl.get(), STOP) ||
                         (Thread.interrupted() &&
                          runStateAtLeast(ctl.get(), STOP))) &&
                        !wt.isInterrupted())
                        wt.interrupt();
                    try {
                        beforeExecute(wt, task);
                        Throwable thrown = null;
                        try {
                            task.run();
                        } catch (RuntimeException x) {
                            thrown = x; throw x;
                        } catch (Error x) {
                            thrown = x; throw x;
                        } catch (Throwable x) {
                            thrown = x; throw new Error(x);
                        } finally {
                            afterExecute(task, thrown);
                        }
                    } finally {
                        task = null;
                        w.completedTasks++;
                        w.unlock();
                    }
                }
                completedAbruptly = false;
            } finally {
                processWorkerExit(w, completedAbruptly);
            }
        }
    

    主要执行的细节方法在runWorker中,而对于发生异常的处理有两个地方afterExecute,processWorkerExit。afterExecute是留给子类实现的,processWorkerExit的源码如下

    /**
         * Performs cleanup and bookkeeping for a dying worker. Called
         * only from worker threads. Unless completedAbruptly is set,
         * assumes that workerCount has already been adjusted to account
         * for exit.  This method removes thread from worker set, and
         * possibly terminates the pool or replaces the worker if either
         * it exited due to user task exception or if fewer than
         * corePoolSize workers are running or queue is non-empty but
         * there are no workers.
         *
         * @param w the worker
         * @param completedAbruptly if the worker died due to user exception
         */
        private void processWorkerExit(Worker w, boolean completedAbruptly) {
        	//如果发生异常,completedAbruptly值为初始化值true,这里会减少目前的工作线程数,目的是为了排除当前发生异常的线程
            if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
                decrementWorkerCount();
    
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                completedTaskCount += w.completedTasks;
                workers.remove(w);
            } finally {
                mainLock.unlock();
            }
    
            tryTerminate();
    
            int c = ctl.get();
            if (runStateLessThan(c, STOP)) {
                if (!completedAbruptly) {
                    int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                    if (min == 0 && ! workQueue.isEmpty())
                        min = 1;
                    if (workerCountOf(c) >= min)
                        return; // replacement not needed
                }
                //这里会新增一个工作线程
                addWorker(null, false);
            }
        }
    

    至此终于线程池的发生异常的处理过程大致就清楚了,从此例可以看出,ThreadPoolExecutor默认使用的是懒加载的方式,没有一开始就初始化指定核心线程数量的线程。而Worker同时也是装饰模式的一种应用,给Tread增加了一些额外的处理逻辑,使其使用起来更加灵活。

    展开全文
  • 分段执行业务时需要执行某段sql语句,由于是多个线程执行多个sql,线程之间需要使用一些读锁,若发生异常需要释放锁,结果某个线程的sql出现了错误,后台没有打印错误日志。 List<ImportModel> query = ...

    背景

    分段执行业务时需要执行某段sql语句,由于是多个线程执行多个sql,线程之间需要使用一些读锁,若发生异常需要释放锁,结果某个线程的sql出现了错误,后台没有打印错误日志。

    List<ImportModel> query = query(dataTransfer.getEngName(), sql);
                    try {
                        readLock.lock();
                        arrayList.add(query);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    finally {
                        readLock.unlock();
                    }

     

    测试结果

    对平时使用多线程的方式进行测试,结果发现和是否执行get方式有关,测试结果如面代码。

    public Object test(String code) throws Exception{
    
            //没有抛出异常
            queryPool.submit(()->{
                try {
                    log.info(Thread.currentThread().getName()+"的线程正在运行运行");
                    int i=1/0;
                }finally {
                    log.info("执行完毕");
                }
            });
    
            //抛出了异常
            queryPool.submit(()->{
                try {
                    log.info(Thread.currentThread().getName()+"的线程正在运行运行");
                    int i=1/0;
                }finally {
                    log.info("执行完毕");
                }
            }).get();
    
            //抛出了异常
            new Thread(()->{
                log.info(Thread.currentThread().getName()+"的线程正在运行运行");
                int i=1/0;
            }).start();
    
            //抛出了异常
            queryPool.submit(new MyThread());
    
            //抛出了异常
            queryPool.submit(new MyThread()).get();
    
    
            return null;
        }
    
        class MyThread implements Runnable{
            @Override
            public void run() {
                log.info(Thread.currentThread().getName()+"的线程正在运行运行");
                int i=1/0;
            }
        }

     

    这种常规错误可以正常抛了,但是改成mapper查询却不行了

     queryPool.submit(()->{
                try {
                    log.info(Thread.currentThread().getName()+"的线程正在运行运行");
                    List<ImportModel> importModels = dataTransferMapper.queryDepartment(sql);
                }
                finally {
                    log.info("执行完毕");
                }
            });

    之前是直接在当前方法内抛出异常可以,但是执行某个方法内部抛异常外界却拿不到,设置一个对照实验,在一个run方法内部写一个异常代码,结果果然是外界无法拿到

    queryPool.submit(()->{
                try {
                    log.info(Thread.currentThread().getName()+"的线程正在运行运行");
                    run();
                }
                finally {
                    log.info("执行完毕");
                }
            });
    
    public static void run(){
            int i=1/0;
        }

     

    所以问题不是是否get的问题,而是一些不带throw Exception的方法,多线程执行的话最好要加上catch,否则拿不到异常信息,外界不知道发生了异常。

     

    展开全文
  • 捕获Java线程池执行任务抛出异常Java中线程执行的任务接口java.lang.Runnable 要求不抛出Checked异常, public interface Runnable { public abstract void run(); } 那么如果 run() 方法中抛出了...

    捕获Java线程池执行任务抛出的异常
    Java中线程执行的任务接口java.lang.Runnable 要求不抛出Checked异常,
      public interface Runnable {
      public abstract void run();

      }
      那么如果 run() 方法中抛出了RuntimeException,将会怎么处理了?
      通常java.lang.Thread对象运行设置一个默认的异常处理方法:
      java.lang.Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler)
       而这个默认的静态全局的异常捕获方法时输出堆栈。
      当然,我们可以覆盖此默认实现,只需要一个自定义的java.lang.Thread.UncaughtExceptionHandler接口实现即可。
      public interface UncaughtExceptionHandler {
      void uncaughtException(Thread t, Throwable e);
      }

      而在线程池中却比较特殊。默认情况下,线程池 java.util.concurrent.ThreadPoolExecutor 会Catch住所有异常, 当任务执行完成(java.util.concurrent.ExecutorService.submit(Callable))获取其结果 时(java.util.concurrent.Future.get())会抛出此RuntimeException。

     

     

    http://blog.sina.com.cn/s/blog_8839cbea0101f5wa.html

    转载于:https://www.cnblogs.com/softidea/p/3865965.html

    展开全文
  • 最近项目中一些异步执行的逻辑没有运行异常却没有打日志 给定位问题带来麻烦?? 问题分析 接下来我们来看一下java中的线程池是如何运行我们提交的任务的,详细流程比较复杂,这里我们不关注,我们只关注任务...
  • Java中线程执行的任务接口java.lang.Runnable 要求不抛出Checked异常,  public interface Runnable {  public abstract void run();  }  那么如果 run() 方法中抛出了RuntimeException,将会怎么处理了? ...
  • 那么就需要用到submit,通过捕获Future.get抛出异常。 public interface ExecutorService extends Executor { ...省略 <T> Future<T> submit(Callable<T> task); 好在java.util.concurrent....
  • 我们使用了Java自带的Executor模块,我只是稍微看了下Executors当中三个线程池的实现(策略为:Fixed, Cached, Schedule),其实光看名字就可以了解各自的一些策略信息。OK,这一次我需要一种策略合并Fixed和Cached....
  • 可以通过ThreadFactory自定义线程并捕获线程内抛出异常,也就是说甭管我们是否去捕获和处理线程池中工作线程抛出异常,这个线程都会从线程池中被移除 源码 这道面试题源码在ThreadPoolExecutor#runWorker()方法...
  • package 线程池; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.... * 主线程捕捉线程池中线程抛出异常 * @author kevin4wang */ /** * E
  • 线程池中线程异常如何处理?

    千次阅读 2021-03-08 22:02:52
    模拟线程池抛异常2. 如何获取和处理异常方案一:使用 try -catch方案二:使用Thread.setDefaultUncaughtExceptionHandler方法捕获异常方案三:重写afterExecute进行异常处理 1. 模拟线程池抛异常   &...
  • 背景 ...通过查找资料和demo测试,发现ScheduledThreadPool这个定时线程池抛出异常的任务会进行挂起,此后不会再进行调用。 解决 在任务中对任务进行合规校验 可对不可避免的异常进行捕获,避免异
  • 线程池的诡异异常

    2019-06-24 19:18:33
      现象:线程池使用时,任务中抛出一个没有捕获的异常时,线程池没有挂掉,并且任务也没有继续运行。      代码模拟: ExecutorService executor = Executors.newSingleThreadExecutor(); executo...
  • 线程池执行异常不打印日志

    千次阅读 2020-06-07 16:43:07
    背景 平时自己在使用的ThreadPoolExecutor的...在进行一波调试之后,任务在线程中出现异常了,但也并未出现异常抛出的情况。 代码测试 1、先用execute 查看当出现异常的情况 public class ThreadPoolTest { priv
  • 线程池中捕获线程执行异常

    千次阅读 2015-08-17 16:32:37
    在项目中使用线程池时,使用了如下方式:ExecutorService dataUploadPool = Executors.newFixedThreadPool(writeThreadNum);dataUploadPool.execute(new Thread(task))但是这种方式下,主线程无法捕获子线程中的异常...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 75,162
精华内容 30,064
关键字:

线程池抛出异常重新执行