互联网复习五(线程池)

Posted 天枰喜爱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了互联网复习五(线程池)相关的知识,希望对你有一定的参考价值。

// 查看硬件的核数Runtime.getRuntime().availableProcessors();

为什么用线程池,优势?

作用:控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。

优势:线程复用、控制最大并发数、管理线程。

线程调用4种方式:

1、继承Thread对象

重写 run 方法

2、实现Runnable接口

实现 run 方法,该方法没有返回值
不抛出异常
调用方式不同
new Thread(() -> {}, "Thread-1").start();

3、实现Callable<V>接口

实现 call 方法,该方法有返回值
抛出异常
/** * 线程操作 */class CallableDemo implements Callable<Cake>{
@Override public Cake call() throws Exception { // 业务处理 Cake cake = new Cake("白雪公主", "90元"); return cake; }
public void test() throws ExecutionException, InterruptedException { FutureTask<Cake> futureTask = new FutureTask<>(new CallableDemo()); new Thread(futureTask, "Thread-1").start(); //相同的事情,不会做重复的事情 //new Thread(futureTask, "Thread-2").start();
while ( !futureTask.isDone() ){ // 自循等待线程完成操作 }
// 获取线程完成后返回结果值 Cake cake = futureTask.get(); }}

4、获取线程,通过线程池获取线程
Excutors 快速获取线程池,通常有 下几种:

Excutors.newScheduledThreadPool();

// 使用目前机器上可用的处理器作为它的并发级别

Excutors.newWorkStealingPool(int);

// 一池固定线程

Excutors.newFixedThreadPool(int);

// 一池一线程

Excutors.newSingleThreadExcutor();

互联网复习五(线程池)

// 一池多线程

Excutors.newCachedThreadPool();

互联网复习五(线程池)


线程池底层实现:

1、ThreadPoolExcutor类使用

2、阻塞队列

互联网复习五(线程池)

7大参数介绍:

1、corePoolSize 线程池中常驻核心线程数

2、maxnumPoolSize 线程池能够容纳同时执行最大线程数,此值必须大于等于1

3、keepAliveTime 多余的空闲线程的存活时间;当前线程池数量超过corePoolSize时,当空闲时间达到keepAliveTime值时,多余空闲线程会被销毁直到只剩下corePoolSize个线程为止

4、unit keepAliveTime单位

5、workQueue 任务队列,被提交但尚未被执行的任务

6、threadFactory 表示生成线程池中工作线程的线程工厂,用于创建线程一般默认

7、handler 拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数(maxnumPoolSize)时如何拒绝请求执行的runnable的策略

业务理解:

1、在创建了线程池后,等待提交过来的任务请求

2、当调用execute()方法添加一个请求任务时,线程池会做如下判断:

2.1、如果正在远行线程数量小于corePoolSize,那么马上创建线程远行这个任务
2.2、如果正在远行线程数量大于等于corePoolSize,那么将这个任务放入队列
2.3、如果这个时候队列满了且正在远行的线程数量还小于maxnumPoolSize,那么还是要创建非核心线程立即运行这个任务

2.4、如果队列满了且正在远行的线程数量大于maxnumPoolSize,那么线程池会启动饱和拒绝策略来执行

3、当一个线程完成任务时,它会从队列取下一个任务来执行

4、当一个线程无事可做超过一定时间(keepAliveTime)时,线程池会判断

如果当前远行的线程数大于corePoolSize,那么这个线程就会被销毁
所以线程池的所有任务完成后,最终会收缩到corePoolSize大小

工作中是如何使用线程池的?是否自定义过线程池使用?

1、数据库线程池

2、redis线程池

自己定义代码:

ExecutorService threadPool = new ThreadPoolExecutor(                3, // corePoolSize 核心线程数 5, // maxnumPoolSize 同执行最大线程数                1L,  // keepAliveTime 闲置线程存活时间                TimeUnit.SECONDS, // keepAliveTime 单位                new LinkedBlockingDeque<>(3), // 阻塞队列 等待区 Executors.defaultThreadFactory(), // 创建线程工厂                new ThreadPoolExecutor.AbortPolicy()); // 拒绝策略(抛异常)
for (int i = 0; i < 10; i++) { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + " 办理业务"); });}

运行结果:

pool-1-thread-1 办理业务pool-1-thread-1 办理业务pool-1-thread-1 办理业务Exception in thread "main" pool-1-thread-1 办理业务pool-1-thread-2 办理业务java.util.concurrent.RejectedExecutionException: Task com.xiai.mini.TestMain$$Lambda$1/540585569@78c03f1f rejected from java.util.concurrent.ThreadPoolExecutor@5383967b[Running, pool size = 5, active threads = 3, queued tasks = 0, completed tasks = 5] at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047) at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369) at com.xiai.mini.TestMain.main(TestMain.java:40)pool-1-thread-3 办理业务pool-1-thread-4 办理业务pool-1-thread-5 办理业务

合理配置线程池是任何考虑的?

CPU密集型:CPU核数+1

IO密集型:

1、CPU核数 * 2
2、阻塞系数在0.8~0.9之间
公式:CPU核数 / (1 - 阻塞系数)

死锁编码及定位分析?

指两个或者两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若外力干涉那它们都将无法推进下去

主要原因:系统资源不足、进程运行推进的不合适、资源分配不当

实现代码:

class DeadThreadDemo implements Runnable{
private String lockOne; private String lockTwo;
public DeadThreadDemo(String lockOne, String lockTwo) { this.lockOne = lockOne; this.lockTwo = lockTwo; }
@Override public void run() { synchronized (lockOne){ System.out.println(Thread.currentThread().getName() + " 获取到锁:" + lockOne + ", 但尝试获取" + lockTwo);
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockTwo){ System.out.println(Thread.currentThread().getName() + " 获取到锁:" + lockTwo + ", 但尝试获取" + lockOne); } } }
public static void main(String[] args){ String lockOne = "lock-One"; String lockTwo = "lock-Two";        new Thread(new DeadThreadDemo(lockOne, lockTwo)).start();        new Thread(new DeadThreadDemo(lockTwo, lockOne)).start();    }}

运行结果:

解决:

1、jps命令定位进程号
2、jstack找到死锁查看


以上是关于互联网复习五(线程池)的主要内容,如果未能解决你的问题,请参考以下文章

Java大数据面试复习30天冲刺 - 日积月累,每日五题Day04——JavaSE

newCacheThreadPool()newFixedThreadPool()newScheduledThreadPool()newSingleThreadExecutor()自定义线程池(代码片段

和朱晔一起复习Java并发:线程池

python 复习—并发编程——线程锁threading.local线程池生产者消费者模型线程安全

Python并发复习4- concurrent.futures模块(线程池和进程池)

简单线程池原理和代码