Java--线程池

频繁的启动关闭一个线程的成本是很高的,而Java线程依赖于内核线程,创建线程需要进行操作系统状态切换,所以才有了线程池的概念,通过在初始化的时候创建大量的线程,放在线程池中,程序将一个Runnable对象传给线程池,线程会启动一个线程去执行他们的run方法。当执行完毕后,该线程不会马上死亡,而是会交还给线程池中,等待下一个。
主要的线程池有:
ExecuteService:代表尽快执行任务的线程池。
ScheduledExecutorService:可以延后或指定周期执行任务的线程池。
入门级例子:

public static void main(String[] args) {
		
		//创建固定线程数为6的线程池
		ExecutorService executorService = Executors.newFixedThreadPool(6);
		
		//添加两个认为到线程池中
		executorService.submit(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 100; i++) {
					System.out.println(Thread.currentThread().getName()+" i="+i);
				}
			}
		});
		
		executorService.submit(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 100; i++) {
					System.out.println(Thread.currentThread().getName()+" i="+i);
				}
			}
		});
	}

用完线程池后,线程池中所用的线程不会死亡。
shutdown:不允许提交任务,将那些空闲的线程比如正在从队列中获取任务的线程设置中断标识位,
所有的线程不会立刻死亡,直到所有的线程池中的线程都执行完了任务。
shutdownNow:不允许提交任务,将所有正在执行中的线程设置中断标识位,不允许从队列中获取任务了。
会立刻关闭线程池。
参考《疯狂java讲义》。.

简单应用,想数据库的表中插入数据,分10个线程执行。

class InsertThread implements Runnable {

	private Integer beginName;

	public InsertThread(Integer beginName) {
		this.beginName = beginName;
	}

	@Override
	public void run() {
		int count = 1;
		while (count <= 10000) {
			StringBuffer insertSql = new StringBuffer();
			insertSql.append(" INSERT INTO user(name,password,createtime) ");
			SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String nowDate = formatter.format(new Date());
			insertSql.append(" VALUES('"+this.beginName+"','123',' " + nowDate + " ') ");
			JdbcUtils.insert(insertSql.toString());
			count++;
			this.beginName++;
		}
	}
}

public class InsertThreadTestMain {
	public static void main(String[] args) {
		// 创建固定线程数为10的线程池
		ExecutorService executorService = Executors.newFixedThreadPool(10);
		InsertThread insertThread = null;
		for (int i = 1; i < 11; i++) {
			insertThread = new InsertThread(i + 10000);
			executorService.submit(insertThread);
		}
		System.out.println("run over");
	}
}

在添加的线程过多时,而线程中存储的线程有限,线程池有拒绝策略,通过回调方法可以做相应的处理

public class RejectThreadPoolDemo {

	public static void main(String[] args) {
		ExecutorService executorService = new ThreadPoolExecutor(5, 5, 0L,
				TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(),
				Executors.defaultThreadFactory(),
				new RejectedExecutionHandler() {
					@Override
					public void rejectedExecution(Runnable r,
							ThreadPoolExecutor executor) {
						System.out.println(r.toString() + " is discard");
					}
				});

		for (int i = 0; i < 10; i++) {
			executorService.submit(new Task());
		}
	}

	public static class Task implements Runnable {
		@Override
		public void run() {
			System.out.println("ThreadId=" + Thread.currentThread().getId());
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

这里写图片描述
如图,创建的一个线程池最大可用线程为5个,而每一个任务中通过睡眠当前任务,导致5个任务一直占用该线程,后续的任务无法添加到任务,可查看定义线程池时的回调方法。

打印当前线程池中任务执行情况和线程数:

private static void printThreadPoolInfo(){
        logger.info("=============打印线程信息======");
        ThreadPoolExecutor tpe = ((ThreadPoolExecutor) HandleBatchUploadIvrManager.threadPool);
        logger.info("线程池执行完的任务数:【{}】",tpe.getCompletedTaskCount());
        logger.info("线程池核心数:【{}】",tpe.getCorePoolSize());
        logger.info("线程池队列中陷入等待的任务个数:【{}】",tpe.getQueue().size());
        logger.info("线程池最大线程数:【{}】",tpe.getLargestPoolSize());
        logger.info("线程池活跃线程数:【{}】",tpe.getPoolSize());
        logger.info("线程池执行任务的个数:【{}】",tpe.getActiveCount());
        logger.info("线程池执行过的任务数:【{}】", tpe.getTaskCount());
    }

UML继承关系
在这里插入图片描述
AbstractExecutorService

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        //构造FetureTask
        RunnableFuture<Void> ftask = newTaskFor(task, null)
        execute(ftask);
        return ftask;
    }

execute

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        //当前线程数是否小于核心线程数
        if (workerCountOf(c) < corePoolSize) {
        	//添加任务到set容器中并且执行。
            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);
    }

addWorker

    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            //线程池执行状态大于等于关闭状态并且满足等于关闭状态,firstTask不为空或者workQueue为空,直接拒绝
            //  ! workQueue.isEmpty() 不为空可以添加,如果为空,那就意味着任务执行完成,不允许添加任务了。
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                //当前执行线程数大于最大,或者大于核心线程数或者最大线程数,返回
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //增加执行任务数
                if (compareAndIncrementWorkerCount(c))
                    //跳出两层循环。
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    //重新再试一次
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    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) {
                    //执行封装的Worker,先执行当前的任务,然后重队列中获取。执行run方法。
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
    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 {
                        //执行的是传递进去的接口run方法
                        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);
        }
    }
    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
			//STOP状态是不允许从队列中获取任务执行的
			//在SHUTDOWN状态下队列不为空,还是可以继续执行任务的。
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);
            //当前线程数大于最大线程数,不允许获取任务,让当前线程死亡。
            //或者从队列中获取任务超时,也会返回获取任务失败,让当前线程死亡。
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                //从队列中获取任务。
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

流程为启动线程,线程执行runWorker方法,执行的是传递进去的Runnable接口的run方法。执行完成后从队列中获取任务执行。

线程池关闭

    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            //设置线程池的状态为ShUTDOWN状态
            advanceRunState(SHUTDOWN);
            //设置没有在跑任务的线程中断标识
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }
    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
             //设置线程池的状态为STOP状态
            advanceRunState(STOP);
            interruptWorkers();
            //将未执行完的任务返回
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }

shutdown和shutdownNow区别
1 设置线程池的状态不同,shutdown是将状态设置为shutdown,而shutdownNow是将线程池执行状态设置为了STOP。
2 由于1的区别,导致执行shutdown后,只要队列不为空,将会继续从队列中获取任务执行,而shutdownNow方法将会把队列中的任务放到一个集合中,然后返回。队列中没有任务了。线程自然而然的死亡了。

如果执行 任务异常,会出现线程销毁,导致频繁的创建。
在这里插入图片描述
在创建线程的工厂创建线程的时候指定线程执行出现异常的时候,JVM会调用UncaughtExceptionHandler的uncaughtException处理异常,
在这里插入图片描述
但是异常还是会往外抛,只有在执行任务的时候手动try catch。
2022年2约15日更新
测试代码

   /**
     * 2022-02-15 13:52:23,436 INFO [com.txp.owner.basic.record.test202202.TestMain20220210] [main] - 创建线程
     * 2022-02-15 13:52:23,437 INFO [com.txp.owner.basic.record.test202202.TestMain20220210] [main] - 创建线程
     * 2022-02-15 13:52:23,437 INFO [com.txp.owner.basic.record.test202202.TestMain20220210] [main] - 创建线程
     * 2022-02-15 13:52:23,437 INFO [com.txp.owner.basic.record.test202202.TestMain20220210] [customer-1] - 开始执行
     * 2022-02-15 13:52:23,437 INFO [com.txp.owner.basic.record.test202202.TestMain20220210] [customer-2] - 开始执行
     * 2022-02-15 13:52:23,437 INFO [com.txp.owner.basic.record.test202202.TestMain20220210] [customer-1] - 创建线程
     * 2022-02-15 13:52:23,437 INFO [com.txp.owner.basic.record.test202202.TestMain20220210] [main] - 创建线程
     * 2022-02-15 13:52:23,438 INFO [com.txp.owner.basic.record.test202202.TestMain20220210] [customer-3] - 开始执行
     * 2022-02-15 13:52:23,438 INFO [com.txp.owner.basic.record.test202202.TestMain20220210] [customer-3] - 创建线程
     * 2022-02-15 13:52:23,438 INFO [com.txp.owner.basic.record.test202202.TestMain20220210] [customer-2] - 创建线程
     * 2022-02-15 13:52:23,438 INFO [com.txp.owner.basic.record.test202202.TestMain20220210] [main] - 创建线程
     * 2022-02-15 13:52:23,439 ERROR [com.txp.owner.basic.record.test202202.TestMain20220210] [customer-3] - 线程customer-3发生异常
     * java.lang.ArithmeticException: / by zero
     * 	at com.txp.owner.basic.record.test202202.TestMain20220210$3.run(TestMain20220210.java:63)
     * 	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:748)
     * 2022-02-15 13:52:23,454 INFO [com.txp.owner.basic.record.test202202.TestMain20220210] [customer-8] - 开始执行
     * 2022-02-15 13:52:23,454 INFO [com.txp.owner.basic.record.test202202.TestMain20220210] [customer-8] - 创建线程
     * 2022-02-15 13:52:23,455 ERROR [com.txp.owner.basic.record.test202202.TestMain20220210] [customer-8] - 线程customer-8发生异常
     * 异常一直会往外抛,会不停的创建新的线程
     * @throws InterruptedException
     */
    @Test
    public void test02() throws InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
                10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadFactory() {

            private final AtomicInteger threadNumber = new AtomicInteger(1);

            @Override
            public Thread newThread(Runnable r) {
                log.info("创建线程");
                Thread t = new Thread(r, "customer-" + threadNumber.getAndIncrement());
                //设置执行异常处理方法-这里执行异常打印,不能控制异常不往外面抛
                t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                    @Override
                    public void uncaughtException(Thread t, Throwable e) {
                        log.error(String.format("线程%s发生异常", t.getName()), e);
                    }
                });
                return t;
            }
        });
        for (int i = 0; i < 10; i++) {
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    log.info("开始执行");
                    log.info("result:{}", 1 / 0);
                }
            });
        }
        threadPoolExecutor.shutdown();
        while (true) {
            if (threadPoolExecutor.isTerminated()) {
                break;
            }
            Thread.sleep(500);
        }
        log.info("执行完成");
    }

实现afterExecute方法,可以打印执行的异常,同时JVM还是会执行u’n’caughException方法

    public void uncaughtException(Thread t, Throwable e) {
        if (parent != null) {
            parent.uncaughtException(t, e);
        } else {
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }

参考https://mp.weixin.qq.com/s/X6TIaiLiBHnfIVrju4q-CQ

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值