多线程在什么场景下需要使用,及其设置corePool线程池大小多大,配置什么补偿策略,
为什么要是用线程池?
线程是操作系统能够运行调度的最小单位。
当应用场景为,计算密集型时:为了将每个cpu充分利用起来,线程数量正常是cpu核数+1,还可以看jdk的使用版本,1.8版本中可以使用cpu核数*2。
当应用场景为,io密集型时:做web端开发的时候,涉及到大量的网络传输,不进入持,缓存和与数据库交互也会存在大量io,当发生io时候,线程就会停止,等待io结束,数据准备好,线程才会继续执行,所以当io密集时,可以多创建点线程,让线程等待时候,其他线程执行,更高效的利用cpu效率,他有一个计算公式,套用公式的话,双核cpu理想的线程数就是20。
普通线程需要频繁的创建,执行,销毁三步,线程池的作用能大大减少创建和销毁,使线程在执行的时候不调用创建和销毁,提高线程执行效率,合理的设置线程数,复用线程,避免频繁回收和创建,提高系统运行速率和吞吐量。
线程的核心字段:corePoolSize核心线程数,maxPoolSize最大线程数,QueueCapacity队列大小,keepAliveSecond线程最大空闲时间。
threadNamePrefix设置线程名字。
线程的常用监控状态:
ThreadPoolExecutor :
活跃线程数:activeCount 线程总数:poolSize 需要执行的任务数:taskCount
已完成任务数:compatedTaskCount 曾经创建过最大的线程数:largePoolSize
继承ThreadPoolExcutor:可以重写线程的生命周期函数,afterExecutor,beforeExecutor,执行任务前调用,执行任务后调用,退出任务调用。
线程池的处理流程:
- 提交任务到线程池,查看核心线程是否都在执行,若没有,则执行当前线程,都在执行的话就2
- 查看核心线程数是否满了,若没有满,创建,若满了,执行3
- 查看队列大小是否满了,若没有满,放入队列,若满了,执行4
- 查看线程池是否满了,没满创建一条线程,满了的话执行相应的拒绝策略。
根据实际场景,可以选择合适自己的拒绝策略,设置rejectedExecutionHandler来配置拒绝策略可以实现接口自定义,jdk自带的有四种拒绝策略:
- 拒绝处理策略:用于被拒绝的任务将抛出异常。
- CallerRunsPolicy:如果有被拒绝的任务,直接在调用者所在的线程调用execute()方法。
- DiscardOldestPolicy:如果有被拒绝的任务,选择生命周期存在时间最长的任务放弃。
- DiscardPolicy:如果有被拒绝的任务,默认直接放弃此任务。
实现多线程的方法有哪些?
- 可以用类实现runable接口,实例化new Thread(),在括号放入target,调用.start启动。
- 可以继承thread类,实例化类调用.start()启动线程。
- 可以实现CallAble来重写call方法,前两个是重写run(),此方法可以有返回值,用FatureTask来启动,返回值用result.get()获取。
- Jdk1.5之后引入executor框架线程池来实现,解耦了提交和执行线程,用户只需要提交线程,其他什么时候执行,由谁执行,都交给了ExecutorService。
Executors可以创建四个不同的线程池:
单个串行执行Executors.newSingleThreadExecutor:线程出现异常重新创建线程。
固定线程数量线程池Executors.newFixedThreadExecutor:可以指定线程数量。
缓存线程池Executors.newCacheThreadExecutor:可以延迟执行,执行有返回值的任务。
无限制线程池Executors.newScheduleThreadExecutor:大小无限制,适合周期性,定时器。
Executors提交任务有两种方式:
Execute():无返回值。
Submit():有返回值。
停止线程的两种方式:
shutDown():将线程池状态设置为shutDown,停止所有未执行的线程,并将正在执行的线程执行完。
shutDownNow():将线程池状态设置为stop,并尝试停止所有线程,可能会导致线程执行一半停止,未执行完。
CountDownLatch():可以使用countDownLatch,countDownLatch数量(线程核心数,任务数),通过构造方法吧数量传到实现runable的线程类里,每次执行线程完毕一次,调用countDown()-1,最后不需要和之前那样调用executorService.shotDown()关闭线程池,调用countDownLatch.wait()方法。
优点:允许一个或多个线程等待直到其他线程完成一组操作的同步辅助。
我的理解是,可以在wait之后写需要执行的代码块,此代码块必定会在等待其他线程执行完之后执行,若不用countDownLatch,此处的代码块可能会在与其他线程同步执行。
wait就是当countDOwnLatch数量为0时才能继续执行。
Sleep():不会释放锁,规定时间内阻塞线程。
Wait():释放锁,线程进入等待,将此线程放入线程池中,等待其他线程调用notify() 或者notifyAll()唤醒。
Join():释放锁,因为源代码里包含了wait(),等待线程该线程终止,主线程等待子线程执行完毕。
Yield():不会释放锁,暂停当前线程,让自己同优先级,优先级更高的线程先执行。
注意:wait(),notifyAll,notify()不属于thread类,属于object,因为他们都必须标明所属的锁,而锁又是任意对象,能被任意对象调用的类一定属于Object。
本文代码重点:锁的唤醒机制(wait,notifly的使用)。
当功能需要读和写的时候,若数据为空,读的时候则让其等待,等待写的进程写入数据,在将读的进程唤醒。
package com.miu.web.threadCon;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class WaitNotifyThread {
/*
* 唤醒等待代码
*
* */
public static void main(String[] args) {
UserClass userClass = new UserClass();
InputClass inputClass = new InputClass(userClass);
OutputClass outputClass = new OutputClass(userClass);
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(inputClass);
executorService.execute(outputClass);
executorService.shutdown();
}
static class InputClass implements Runnable{
private UserClass userClass;
public InputClass(UserClass userClass){
this.userClass=userClass;
}
@Override
public void run() {
while(true){
synchronized (userClass){
if(userClass.flag){
System.out.println("Input等待进入");
try{
userClass.wait();
}catch (Exception e){
}
}
userClass.setName("张三");
userClass.setAge("18");
UserClass.flag = true;
userClass.notify();
}
}
}
}
static class OutputClass implements Runnable{
private UserClass userClass;
public OutputClass(UserClass userClass){
this.userClass=userClass;
}
@Override
public void run() {
while(true){
synchronized (userClass){
if(!userClass.flag){
System.out.println("OutPut等待进入");
try{
userClass.wait();
}catch (Exception e){
}
}
System.out.println("输出:"+userClass.getName()+",age:"+ userClass.getAge());
UserClass.flag = false;
userClass.notify();
}
}
}
}
static class UserClass {
private String name;
private String age;
private static boolean flag = false;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
}