互联网复习五(线程池)
// 查看硬件的核数
Runtime.getRuntime().availableProcessors();
为什么用线程池,优势?
作用:控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。
优势:线程复用、控制最大并发数、管理线程。
线程调用4种方式:
1、继承Thread对象
2、实现Runnable接口
new Thread(() -> {}, "Thread-1").start();
3、实现Callable<V>接口
/**
* 线程操作
*/
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.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.4、如果队列满了且正在远行的线程数量大于maxnumPoolSize,那么线程池会启动饱和拒绝策略来执行
3、当一个线程完成任务时,它会从队列取下一个任务来执行
4、当一个线程无事可做超过一定时间(keepAliveTime)时,线程池会判断
工作中是如何使用线程池的?是否自定义过线程池使用?
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() + "\t 办理业务");
});
}
运行结果:
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密集型:
死锁编码及定位分析?
指两个或者两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若外力干涉那它们都将无法推进下去
主要原因:系统资源不足、进程运行推进的不合适、资源分配不当
实现代码:
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() + "\t 获取到锁:" + lockOne + ", 但尝试获取" + lockTwo);
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (lockTwo){
System.out.println(Thread.currentThread().getName() + "\t 获取到锁:" + 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();
}
}
运行结果:
解决: