在目前我们常用的springboot项目中。我们一般会将线程池交由spring进行管理:
@EnableAsync
@Component
public class AsyncThreadPool {
@Bean
public ThreadPoolTaskExecutor initializeThreadPool() {
ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor ();
threadPool.setCorePoolSize(5);
threadPool.setMaxPoolSize(10);
threadPool.setQueueCapacity(1000);
threadPool.setKeepAliveSeconds(30);
threadPool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
threadPool.initialize();
return threadPool;
}
}
ThreadPoolTaskExecutor executor = threadPool.initializeThreadPool();
Task task = newTask();
executor.execute(task);
class Task implements Runnable{
@Override
public void run() {
executor.shutdown();
}
}
线程池的工作机制:
如果线程池工作线程数 如果线程池工作线程数>=corePoolSize并且等待队列未满,将task插入等待队列。 如果线程池工作流程数>=corePoolSize并且等待队列已满,且工作线程数 如果线程池工作流程数>=corePoolSize并且等待队列已满,且工作线程数=maximumPoolSize,执行拒绝策略。 构造函数的参数含义如下: corePoolSize:指定了线程池中的线程数量,它的数量决定了添加的任务是开辟新的线程去执行,还是放到workQueue任务队列中去; maximumPoolSize:指定了线程池中的最大线程数量,这个参数会根据你使用的workQueue任务队列的类型,决定线程池会开辟的最大线程数量; keepAliveTime:当线程池中空闲线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁; unit:keepAliveTime的单位 workQueue:任务队列,被添加到线程池中,但尚未被执行的任务;它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种; threadFactory:线程工厂,用于创建线程,一般用默认即可; handler:拒绝策略;当任务太多来不及处理时,如何拒绝任务; workQueue任务队列 它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列; Rejected tasks 拒绝任务 AbortPolicy:默认测策略,抛出RejectedExecutionException运行时异常; CallerRunsPolicy:这提供了一个简单的反馈控制机制,可以减慢提交新任务的速度; DiscardPolicy:直接丢弃新提交的任务; DiscardOldestPolicy:如果执行器没有关闭,队列头的任务将会被丢弃,然后执行器重新尝试执行任务(如果失败,则重复这一过程); 我们可以自己定义RejectedExecutionHandler,以适应特殊的容量和队列策略场景中。 线程状态 线程状态从大的方面来说,可归结为:初始状态、可运行状态、不可运行状态和消亡状态,具体可细分为上图所示7个状态,说明如下: 1)线程的实现有两种方式,一是继承Thread类,二是实现Runnable接口,但不管怎样,当我们new了Thread实例后,线程就进入了初始状态; 2)当该对象调用了start()方法,就进入可运行状态; 3)进入可运行状态后,当该对象被操作系统选中,获得CPU时间片就会进入运行状态; 4)进入运行状态后涉及的情况就比较多,大致有如下情形: ﹒ run()方法或main()方法结束后,线程就进入终止状态; 当线程调用了自身的sleep()方法或其他线程的join()方法,就会进入阻塞状态(该状态虽停止当前线程,但并不释放所占有的资源)。 当sleep()结束或join()结束后,该线程进入可运行状态,继续等待OS分配时间片; 当线程刚进入可运行状态(注意,还没运行),发现将要调用的资源被锁住(synchroniza,lock),将会立即进入锁池状态,等待获取锁标记(这时的锁池里也许已经有了其他线程在等待获取锁标记,这时它们处于队列状态,既先到先得),一旦线程获得锁标记后,就转入可运行状态,等待OS分配 CPU时间片; 当线程调用wait()方法后会进入等待队列(进入这个状态会释放所占有的所有资源,与阻塞状态不同),进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒(由于notify()只是唤醒一个线程,但我们由于不能确定具体唤醒的是哪一个线程,也许我们需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程),线程被唤醒后会进入锁池,等待获取锁标记。 当线程调用stop方法,即可使线程进入消亡状态, wait和sleep的区别 1. Thread类的方法:sleep(),yield() Object的方法:wait()和notify()、notifyAll() 2. 每个对象都有一个锁来控制同步访问。Synchronized关键字可以和对象的锁交互,来实现线程的同步。 sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。 3. wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用 。注意:wait()必须放在synchronized block中,否则会在program runtime时扔出“java.lang.IllegalMonitorStateException”异常。 4. sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常 综上可得两者最大的区别:sleep()睡眠时,保持对象锁,仍然占有该锁;而wait()睡眠时,释放对象锁。 run和start区别: run只是Thread里面的一个普通方法,start是启动线程的方法 实现了Runnable接口的对象并不是线程,它只是任务。Thread对象才是真正的线程创建者。也就是说,任务和线程是分开的,任务放在线程里面才会被执行 sleep() 和 yield()方法 这两个方法都定义在Thread.java中 sleep()的作用是让当前线程休眠(正在执行的线程主动让出cpu,然后cpu就可以去执行其他任务),即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时候会大于或者等于该休眠时间,当时间过后该线程重新被会形式,他会由“阻塞状态”编程“就绪状态”,从而等待cpu的调度执行,注意:sleep方法只是让出了cpu的执行权,并不会释放同步资源锁。 yield()的作用是让步,它能够让当前线程从“运行状态”进入到“就绪状态”,从而让其他等待线程获取执行权,但是不能保证在当前线程调用yield()之后,其他线程就一定能获得执行权,也有可能是当前线程又回到“运行状态”继续运行,注意:这里我将上面的“具有相同优先级”的线程直接改为了线程,很多资料都写的是让具有相同优先级的线程开始竞争,但其实不是这样的,优先级低的线程在拿到cpu执行权后也是可以执行,只不过优先级高的线程拿到cpu执行权的概率比较大而已,并不是一定能拿到。



