ThreadPoolExecutor 线程池相关学习记录

线程池相关问题
直接使用线程的run方法和start方法的区别 ,在断点方法中点击idea 的照相机的图标按钮,可以查看线程信息
直接运行run
在这里插入图片描述
start方法启动
在这里插入图片描述

线程池的操作效率大于普通线程操作速度,经过代码验证
常见的3种线程池创建方式
ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); // 运行速度最快 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10); // 较慢 ExecutorService executorService = Executors.newSingleThreadExecutor();// 慢 三种创建方式的构造参数都是类似,但是效率却不同的原因如下

ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); // 运行速度最快 其原理同下图 第二个参数Integer.MAX_VALUE 是一个非常大的数字 2147483647 即在这个数字内的线程数,都会快速的处理
在这里插入图片描述

线程的复用
在这里插入图片描述

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10); // 较慢,如图,线程的执行效率和任务处理数在某种意义上成正比,当前的设置是10个线程,处理100个任务, 当初始化线程和执行任务数成正比,且值不大的时候,执行效率很快
在这里插入图片描述

ExecutorService executorService = Executors.newSingleThreadExecutor();// 慢 // 最慢,相当于是单机版的newFixedThreadPool ,因为他不能自定义参数,只有一个线程

在这里插入图片描述

但是以上创建线程的3种方式阿里并不推荐, ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10); // 较慢 ExecutorService executorService = Executors.newSingleThreadExecutor();// 慢 因为 LinkedBlockingQueue 队列,是可以无限存放, 暂时没有人执行的任务全部都会存放在队列里面,假如现在有1000W任务,但是初始线程数量只有10个, 那么剩余的900多W的线程都会放入LinkedBlockingQueue队列,那么服务器的内存就会受不了。 ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); 创建方式也不被推荐的方式就是,如果现在有1000W个任务要执行,那么就会创建1000W个线程,那么cpu就会100%,严重还会崩溃 ,内存泄露 oom异常

自定义线程池的代码实现及分析 // 自定义线程池 ,初始核心员工容量为10,非核心员工10(共计maximumPoolSize=20),自定义队列ArrayBlockingQueue长度为10 // ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 0L, TimeUnit.SECONDS, new ArrayBlockingQueue(10)), 如图所示,线程执行的优先级 是 核心员工,队列,非核心员工
在这里插入图片描述

// 当前的队列存在的问题是,需要同时进行的任务数量一旦超过了定义的30个任务就会向上抛出异常 如图 任务数是需要位置存放的,比如现在核心员工、非核心员工,队列都已经存满了,现在31个任务想要继续进行执行
就会向上抛出异常

在这里插入图片描述

线程池的提交优先级 ,1,核心员工,2队列 3,非核心员工。 线程池的执行优先级,1,核心员工,2,非核心员工,3,队列 ,队列中的任务是最后执行的
线程池的高效工作的秘密 1, 线程复用,2,线程中直接调用的run 方法,而不是start方法
在这里插入图片描述

 try {
// task 线程不为空 ||
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 {
// 验证是否线程结束,时间结束,重掉addworker方法
processWorkerExit(w, completedAbruptly);
}

线程的步骤

在这里插入图片描述

线程的方法区别

interrupt() 优雅的关闭线程

线程的第三种实现方法实现
public class CallAbleThread implements Callable {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
@Override
public Object call() throws Exception {
return “测试线程的返回值”;
}

	public static void main(String[] args) {
		//	用FutureTask 生成对象 CallAbleThread 是我们自定义的类,重写了call 方法

    FutureTask task = new FutureTask<>(new CallAbleThread());
    new Thread(task).start();
    String s = null;
    try {
					//	 task.get(); 用get方法获取返回值 返回类型为object
        s = (String) task.get();
    } catch (InterruptedException e) {
        System.out.println("===异常=> "+e.getMessage());
    } catch (ExecutionException e) {
        System.out.println("===异常=> "+e.getMessage());
    }
    System.out.println("====> "+s);
}

}

为什么使用get方法可以直接获取到返回值
代码如下 在FutureTask 类中的run方法中
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//这里调用的call 方法就是我们自定义类中重写的call方法具体的返回值
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
// 在上述注释中,ran对象被设置成为了true 所以会进入到这个set方法中
// 那么就会将值进行set ,所以get方法就能拿到值了
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}

线程池
在这里插入图片描述
常见问题:
1.execute() 方法和submit()方法的区别
submit()方法可以配合Callable类接受返回值在这里插入图片描述
在这里插入图片描述
自定义线程的拒绝策略
在这里插入图片描述
当执行任务数超过 最大线程数和队列数的和 就会报错,当需要实现自定义的拒绝策略时候,只需要重写 RejectedExecutionHandler 类中的 rejectedExecution 方法

线程池的扩展

	可以自己继承ThreadPoolExecutor类来实现线程池的扩展 
	比如记录线程执行的时间
   static class MyThreadPoolExecutor extends ThreadPoolExecutor{

        private final ThreadLocal<Long> threadLocal=new ThreadLocal<>();

        /**
         * 默认调用父类的构造器
         * @param corePoolSize
         * @param maximumPoolSize
         * @param keepAliveTime
         * @param unit
         * @param workQueue
         */
        public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        }

        @Override
        protected void beforeExecute(Thread t, Runnable r) {
            long nanoTime = System.nanoTime();
            threadLocal.set(nanoTime);
            System.out.println(String.format("%s|beforeNanoTime|time|%s",t.getName(),nanoTime));
            super.beforeExecute(t, r);
        }
        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            long nanoTime = System.nanoTime();
            long totalTime = nanoTime - threadLocal.get();
            System.out.println(String.format("%s|before|time=%s|总共耗时=%s 毫秒",Thread.currentThread().getName(),nanoTime,(totalTime/1000000.0)));
            super.afterExecute(r, t);
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值