一.线程和进程?
- 线程是进程的一部分,线程属于进程,进程包含线程。
- 线程是CPU分配资源的最小单位,进程是操作系统分配资源的最小单位。
- 进程之间相互独立,统一进程下的线程共享进程的内存空间。
二. Java多线程的特点有哪些?
说道这里我们需要先了解一下Java的内存模式是怎样布局的
2.1 原子性
一个或者一组操作,要么同时成功,要么同时失败。在Java内存模型中保证原子性的操作有六种:
read、load、assign、use、store、write。
2.2 可见性
当一个线程修修改了共享变量的值,其他线程能够立即得知到这个修改。Java内存模型是通过在变量进行修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存的方式来实现可见性的。无论是普通变量还是volatile变量都是如此,但是volatile的特殊规则保证了新值能够立即同步到主内存,以及每次使用前立即从主内存刷新。
2.3 有序性
即程序执行的顺序按照代码的顺序来执行。在Java内存模型中允许编译期和处理器对于指令进行重排序,指令重排序不会影响到单线程程序的执行过程,但是会影响到多线程程序的执行的正确性。
如
int a = 100 ; //1
int b = 200 ; //2
int c = a + b ; //3
在正常执行情况下执行的顺序应该是1>>2>>3,但是有时候为了提高整体的执行效率会执行指令重排序,导致执行的顺序可能是2>>1>>3也可能是3>>1>>2,但是也不能是什么都进行重排,是在保证最终结果和代码顺序执行结果一致的情况下才可能进行重排。
三.多线程有什么好处?
提高系统资源的利用率。 打个比方,加入你们一堆朋友打算进饭店吃饭,进程就是桌子,线程就是人。
- 单进程单线程 饭店只有一张桌子,一张桌子只能容纳一个人吃饭。
- 单进程多线程 饭店只有一张桌子,一张桌子可以坐多个人。
- 多进程多线程 饭店有多个桌子,每个桌子都可以做多个人。
四. Java多线程的实现方式
4.1 继承Thread类
不常用
4.2 实现Runnable接口
推荐使用
4.3 继承Calleable接口
run方法可以有返回值,还可以抛出异常
4.4 使用线程池创建
五. 线程池的优点
- 提高线程的利用率,减少创建和销毁线程的次数。
- 根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多线程导致服务器的崩溃。
六. 线程池的创建方式
6.1 ThreadPoolExecutor
ThreadPoolExecutor tpe = new ThreadPoolExecutor(3,10000,1L,
TimeUnit.SECONDS,new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
优势:可以自定义所有参数,根据不同的业务需求从而进行调整。
6.2 Executors
可以通过类的静态方法直接创建配置好的线程池,比较方便。
七.线程池的七大参数
原理:当一个任务来到线程池是会先检查核心线程数是否已经达到最大,如果没有的话就创建一个核心线程来处理该任务。如果核心线程数已经达到最大,那么会继续检查工作队列是否已满,如果工作队列未满,那么将该任务放入到工作队列中,如果工作队列已满,那么就检查当前线程数是否已经达到最大线程数,如果未达到的话就创建一个普通线程来执行这个任务,如果已经达到最大线程数,那么就执行拒绝策略。
7.1 corePoolSize
核心线程的数量,核心线程创建以后不会被销毁。
7.2 maximumPoolSize
最大线程数量,包含了核心线程和非核心线程。
7.3 KeepAliveTime
非核心线程的最大空闲存活时间。
7.4 unit
空闲线程存活时间单位
7.5 WorkQueue 工作队列
有四种类型:
- ArrayBlockingQueue : 基于数组的有界阻塞队列【其大小由创建的时候就指定】,按照FIFO来进行排序
- LinkedBlockingQueue: 基于链表的无界阻塞队列【其最大容量其实是Integer.Max】,也是按照FIFO来进行排序。由于无界,所以可以一直存放不停进来的新任务,而不会去创建非核心线程
- SynchronousQuene:一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
- PriorityBlockingQueue:具有优先级的无界阻塞队列,优先级通过参数Comparator来实现。
7.6 threadFactory
创建一个新线程时所需要使用的工厂,可以用来设定线程名,是否为daemon等。
7.7 handler 拒绝策略
- AbortPolicy: 直接丢弃这个任务并且抛出异常
- DiscardPolicy: 直接丢弃任务,什么也不做。
- DiscardOldestPolicy: 抛弃进入队列最早的那个任务,尝试把这次的任务加入队列
- CallerRunsPolicy: 在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。