java线程池理解

1.线程使用

  ExecutorService executorService = new ThreadPoolExecutor(
                8,
                9,
                1000l,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i <10; i++) {
            executorService.submit(() -> {
                System.out.println("执行多线程任务");
            });
        }

也可以使用

  		//固定大小线程池 底层ThreadPoolExecutor
        Executors.newFixedThreadPool(7);
        //缓存线程池核心线程数为0 先试图将任务加入队列,交给空闲线程处理,使用的是SynchronousQueue(只能有一直取才能的往里面放,但是不能存)
        // 没有空闲在创建一个工作线程 底层ThreadPoolExecutor
        Executors.newCachedThreadPool();
        //定时线程池,继承ThreadPoolExecutor,使用的是DelayedWorkQueue(定时队列,只能容纳RunnableScheduledFutures)
         Executors.newScheduledThreadPool(7);
        //将任务放到本地,当本地队列(先进后取)执行完了 再去窃取别人的队列(后往前取),线程数是默认和cpu核数相等
        Executors.newWorkStealingPool();

但是不建议这样使用,其实这个底层也是new ThreadPoolExecutor,但是参数不可控,可能导致OOM
参数注释

							
 public ThreadPoolExecutor(int corePoolSize,//核心线程
                              int maximumPoolSize,//最大线程数
                              long keepAliveTime,//线程空闲时间
                              TimeUnit unit,//时间单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//线程工厂
                              RejectedExecutionHandler handler) //队列满后拒绝策略

2.为什么要使用线程池

为什么使用多线程
现在电脑都是多核CPU,使用多线程能提高并发效率,也能避免资源浪费
线程是CPU执行的最小单元,java多线程是多个线程竞争cpu执行时间片
多线程能提高效率,但并不一定能提高线程效率,线程过多就会增加线程切换保存线程状态等操作,也会影响计算效率,使用线程池将线程控制和CPU相差不多的情况,就会减少这样的切换;
因为java创建的线程是基于系统的内核线程,创建过程比较消耗性能,使用线程池可以减少创建线程的消耗

3.线程池原理

线程池大概原理就是线程内部维护了一个阻塞队列
1.内部维护了一个队列
2.当线程任务提交后判断工作线程是否到核心线程
2.1没有就创建一个线程,线程内部循环线执行本次任务,队列中取一个执行,如果队列中没有就阻塞
2.2 到达了核心线程数就将任务加入队列,如果队列也满了就调用拒绝策略

基本概念:

workerCount, 有效线程数,不一定是工作线程数
workerCount是已被允许启动且未被允许停止的工作线程数量。该值可能与实际的活动线程数暂时不同,例如,当ThreadFactory在被询问时未能创建线程,而当退出的线程仍在执行终止之前的簿记操作时,该值可能会暂时不同。用户可见的池大小报告为*工作集的当前大小
为了将它们打包为一个int,我们将workerCount限制为(2 ^ 29)-1(约5亿)个线程,而不是(2 ^ 31)-1(2 *十亿)可以表示的线程。如果将来可能出现此问题,可以将该变量更改为AtomicLong ,并在下面调整shift / mask常数。但是直到需要出现时,使用int的这段代码才更快,更简单。

runState, 线程池状态是否正在运行,正在关闭等
runState提供主要的生命周期控制,并具有以下值:

  • RUNNING:接受新任务并处理排队的任务, 标识位 -1<<29=11100000000000000000000000000000
  • SHUTDOWN:不接受新任务,但处理排队的任务, 标识位 0<<29=0
  • STOP:不接受新任务,不执行t处理已排队的任务,*并中断正在进行的任务, 标识位 1<<29=00100000000000000000000000000000
  • TIDYING:所有任务已终止,workerCount为零,线程转换为TIDYING状态将运行Terminated()挂钩方法, 标识位 2<<29=01000000000000000000000000000000
  • TERMINATED:Terminate()已完成* 这些值之间的数字顺序很重要,可以进行有序的比较。 标识位 3<<29 =01100000000000000000000000000000
    取高3位:
    111,000,001,101,011

runState随时间单调增加,但不必达到每个状态。过渡是:

  • RUNNING-> SHUTDOWN 在调用shutdown()时,可能隐式在finalize()中
  • (RUNNING或SHUTDOWN)-> STOP 在调用shutdownNow()
  • SHUTDOWN-> TIDYING 当队列和池都为空时
  • STOP -> TIDYING 当池为空时
  • TIDYING-> TERMINATED *当terminated()挂钩方法完成时

状态为TERMINATED时,在awaitTermination()中等待的线程将返回。 * 检测从SHUTDOWN到TIDYING的转换比您想要的要简单得多因为在SHUTDOWN状态期间队列在非空之后可能变为空,反之亦然,所以队列可能会变为空,但是如果只有在看到队列为空后才能终止,我们看到 workerCount为0
ThreadPoolExecutor中 有个属性
AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
这个设计个人感觉设计非常巧妙
它既能标识线程池状态能标识工作线程数量,前3位就是表示线程池状态,后面29位表示工作线程数量,RUNNING状态值最小,每次增加一个工作线程就+1,当工作线程2的29次方时就不能增加了 就进入SHUTDOWN状态

4.ThreadPoolExecutor源码

线程执行submit是其实调用的java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable)
这里其实比较简单:

 public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        //1.创建任务
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        //2.执行任务
        execute(ftask);
        return ftask;
    }

执行任务在execute

		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) {

        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // 如果工作线程满了 就不能添加了
            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;
                    //cas 修改ct1计数线程
                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 {
        //创建一个工作线程,Worker内部维护了一个Thread ,在构造方法通过ThreadFactory创建
            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;
    
   }

线程工作源码

  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);
        }
    }

5线程池大小

常规设置
1.如果线程是密集计算型,设置cpu核数+1,为了充分利用资源,1核CPU处理1个线程减少CPU线程切换,多一个防止浪费
2.如果io密集型,设置cpu核两倍,因为现在io大部分工作是分派给DMA(Direct Memory Access)直接内存存取 完成的,可以多个线程去切换 cpu
线程大小 具体根据实际需求设置才是最好的;

你可能感兴趣的:(java,多线程)