1、继承Thread
    继承Thread,实现run方法,new Thread().start()创建并执行线程;
    2、实现Runnable接口
    实现Runnable接口,实现run方法,new Thread().start()创建并执行线程;
    3、实现Callable接口+FutureTask(区别去上面,可以拿到返回值,可以处理异常)
    实现Callable接口,重写call()方法;new FutureTask(new Callable()),FutureTask最终也是继承的Runnable
    image.png
    image.pngimage.png
    4、线程池
    4.1 为什么要用线程池?首先考虑,异步的任务非常多的时候,都是写原生代码new Thread().start();
    开启一个线程执行一次start方法(类似于公司有一个新任务了,就招一个人过来,最终会讲公司的资源耗尽)
    这边的业务也是一样的,因为最终给我们分配的堆空间、栈空间,是有限的,如果在高并发系统中,请求一进来,这个业务非常大,要进行非常多的异步任务查询,现在有100万个请求进来,一个业务要开启10个异步任务,这样的话,直接开启一个线程执行一次start方法,执行1000万次,一定会导致资源耗尽,从而导致系统崩溃。
    所以以后在业务代码里面,以上三种启动线程的方式都不使用,而是将所有的多线程异步任务都交给线程池执行。(类似于公司有50个员工,有什么活就分配给这50个员工去做,如果都在忙就等其中一个忙完了,再继续接新的任务)达到了资源控制,消耗的资源也就是这50个人消耗的资源
    4.2 四种线程池
    4.2.1 newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
    4.2.2 newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    4.2.3 newScheduledThreadPool 创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行。
    4.2.4 newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    当前系统中线程池一般设置一两个,每个异步任务,都提交给线程池
    让他们自己去执行
    image.png
    比如当前系统只有一个G 的内存,顶多让200多个线程去执行(峰值),直接给线程池控制好最大量资源,这样,即使有1000万个任务进来,也是200个线程执行一批,完了再执行一批,往复交替,而不是1000万任务全部一下子执行达到资源控制的效果,不会瞬间耗尽资源