多线程
1,线程的创建方式(重点)
集成thread,重写run方法
实现runnable接口,重写run方法
实现callable接口,重写call方法,相对runnable,可以声明返回类型
区别:(次重点)
1,thread方式,最为简单,可以通过this获取当前线程,但是因为继承了thread类,不能在继承其他类了
2,runnable方式,子类实现这个接口,可以继承其他的类,多个线程可以共享同一个target对象,没有返回值
3,callable方式,子类实现这个接口,可以继承其他的类,多个线程可以共享同一个target对象,有返回值
2,thread的状态
3,wait(),yield和sleep()的区别(重点)
sleep:属于thread类,指定休眠时间,不会释放锁,到时间后会自动执行
yield:不指定时间,礼让优先级高的线程执行,也不会释放锁,当别的线程执行完毕后,如果没有优先级更高的线程需要执行,继续执行当前线程,否则继续抢占CPU资源。
Wait:属于object类,一般不指定时间,释放锁,需要通过调用notify或notifyall方法唤醒,才能继续执行(如果给wait设置了等待时间,如果到时间还没有唤醒,则会自动唤醒)
4,进程:系统进行资源分配和调度的一个独立单位
线程: 是比进程更小的可独立运行的基本单位,一般处于进程当中,可以看做是轻量级的进程
5,Thread.Sleep(0) 和不调用sleep的区别:
如果线程中没有调用sleep方法,只要有CPU的控制权,线程会一直将执行模块中的逻辑执行完成,如果调用了sleep方法,将睡眠时间设置为0秒,虽然休眠时间为0,且sleep方法并不会释放锁,但仍然需要重新抢占cup的使用权,系统只是默认为休眠的时间到了。
6,线程的两个核心:JMM内存模型,happens-before
三个特性:1,原子性:即一个操作(有可能包含有多个子操作)要么全部执行(生效),要么全部都不执行(都不生效)
2,可见性:当多个线程并发访问共享变量时,一个线程对共享变量的修改,其它线程能够立即看到
3,有序性:程序执行的顺序按照代码的先后顺序执行
6,死锁:静态的锁顺序死锁,动态的锁顺序死锁,协作对象之间的死锁。在写代码时,要确保线程在获取多个锁时采用一致的顺序。同时,要避免在持有锁的情况下调用外部方法。
7,线程池的优势
①降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗;
②提高系统响应速度,当有任务到达时,无需等待新线程的创建便能立即执行;
③方便线程并发数的管控,线程若是无限制的创建,不仅会额外消耗大量系统资源,更是占用过多资源而阻塞系统或oom等状况,从而降低系统的稳定性。线程池能有效管控线程,统一分配、调优,提高资源使用率;
④更强大的功能,线程池提供了定时、定期以及可控线程数等功能的线程池,使用方便简单。
8,线程池(重点)
线程池的创建:ExecutorService service = new ThreadPoolExecutor(…)
创建线程池时,几个关键参数
corePoolSize:核心线程数,默认情况下,核心线程一直存活在线程池中,即便他们在线程池中处于闲置状态。除非我们将ThreadPoolExecutor的allowCoreThreadTimeOut属性设为true的时候,这时候处于闲置的核心线程在等待新任务到来时会有超时策略,这个超时时间由keepAliveTime来指定。一旦超过所设置的超时时间,闲置的核心线程就会被终止
maximumPoolSize:线程池中所容纳的最大线程数,如果活动的线程达到这个数值以后,后续的新任务将会被阻塞。包含核心线程数+非核心线程数。
keepAliveTime:非核心线程闲置时的超时时长,对于非核心线程,闲置时间超过这个时间,非核心线程就会被回收。只有对ThreadPoolExecutor的allowCoreThreadTimeOut属性设为true的时候,这个超时时间才会对核心线程产生效果
unit:时间单位
workQueue:线程池中保存等待执行的任务的阻塞队列
threadFactory:为线程池提供新线程的创建
handler:无法处理新任务的时候抛出异常
开启线程池的两个方法:execute,没有返回值,不能监听执行结果;submit,会返回一个future,我们可以通过get方法来回去返回值,用于监听线程是否执行完毕。
关闭线程池的两个方法:sutdown,将线程池的状态设置为SHUTDWON状态,中断的是闲置线程。Sutdownnow:将线程池的状态设置为stop,中断所有任务(包括正在执行任务的线程和闲置线程)
线程池的执行原理
如果在线程池中的线程数量没有达到核心的线程数量,这时候就回启动一个核心线程来执行任务。
如果线程池中的线程数量已经超过核心线程数,这时候任务就会被插入到任务队列中排队等待执行。
由于任务队列已满,会立即启动一个非核心线程来执行任务(当然线程池里边目前的线程数量不能超过最大线程数)。
如果线程池中的数量达到了所规定的最大值,那么就会拒绝执行此任务,这时候就会调用RejectedExecutionHandler中的rejectedExecution方法来通知调用者。
9,四种线程池
1,newFixedThreadPool:所有的任务都在核心线程中执行,且核心线程一旦创建,即使任务执行完成,后期一直处于闲置状态,也不会销毁,线程池会一直对这个线程进行维护,直到线程池关闭。如果所有的核心线程都处于活动状态,新加入的任务会被放到linkedblockingqueue这个队列当中,且这个队列没有大小限制。因为核心线程创建后不会被销毁,当新的任务加入时,不需要重新创建线程来执行新的任务,所以相应速度非常快,但是因为核心线程不会销毁,如果长时间处于闲置状态,消耗资源,所以这种线程池一般用于不会有太多任务执行的场景下。
2,newCachedThreadPool:相当于对threadpoolexcutor的一个封装,他的核心线程数量为0,最大线程数量是Integer.MAX_VALUE,线程池对于线程的维护时间是60秒,如果有新的任务,线程池会去内部寻找是否有闲置线程,如果有将任务分发给该线程,如果没有,创建一个非核心线程去执行,当任务执行完成后,线程会等待60秒,如果没有新的任务,将销毁线程。因为最大线程数是一个非常庞大的值,且对于长时间未接受新任务的线程,会回收,所以基本不会达到最大线程的数量,所以内部传入的队列相当于一个空集合,不会使用到。
3,newScheduledThreadPool:是一个可定时执行或周期执行任务的线程池,对于非核心线程,执行完毕后会立马回收(scheduleWithFixedDelay当上一个任务执行完成,然后延时时间到后开始执行第二个任务,因为任务具体的执行时间不能确定,所以除第一个任务外,其他任务的触发时间不可预知;scheduleAtFixedRate当上一个任务开始执行,延时时间到后开始执行第二个任务,因为延时是一个固定时间,所以所有任务的触发时间可推断)
4,newSingleThreadPool:这个线程池当中只有一个核心线程,且最大线程也是1个,同时只能执行一个任务,其他所有任务都放在阻塞队列中,且这个队列没有大小限制(因为没有非核心线程,所以维护时间虽然为0,如果核心线程不设置allowCoreThreadTimeOut为true,维护时间和核心线程没有关系,所以核心线程并不会销毁);