一、线程
1、定义线程有两种方式:扩展Thread类和实现Runnable接口。使用start方法启动线程。
2、线程状态:创建(new),就绪(Runnable),运行(Running),阻塞(Blocked)和终止(dead),状态转换如下图。
3、sleep和wait的区别
1)sleep是Thread类的方法,wait是Object类的方法
2)Thread.sleep不会导致锁行为的改变, 如果当前线程是拥有锁的, 那么Thread.sleep不会让线程释放锁
3)Thread.sleep和Object.wait都会暂停当前的线程. OS会将执行时间分配给其它线程. 区别是, 调用wait后, 需要别的线程执行notify/notifyAll才能够重新获得CPU执行时间。
4、sleep和yield方法区别
1) sleep方法给其他线程运行机会时不会考虑线程的优先级,因此低优先级的线程也有机会运行。yield方法只会给相同优先级或更高优先级的线程以运行的机会
2)线程执行sleep方法后转入阻塞状态,而yield方法执行后转入就绪(ready)状态
3)sleep方法声明抛出InterruptException,而yield方法没有声明任何异常
4) sleep方法比yield方法具有更好的移植性
5、常用方法
start(): 使线程开始执行;java虚拟机调用该线程的run方法
run():运行业务方法
setName():改变线程名称,使之与参数name相同
setPriority():更改线程的优先级
setDaemon(boolean):将改线程标记为守护线程或用户线程;守护线程是在后台执行并且不会阻止JVM终止的线程,当没有用户线程在运行的时候,JVM关闭程序并且退出。
join(long milliesec):等待线程终止的时间最长为millis毫秒,当前线程A等待thread线程终止之后才从thread.join()返回。
interrupt():中断线程
isAlive():测试线程是否处于活动状态
yield():暂停当前正在执行的线程对象,并执行其他线程,放弃时间不确定,不会让出锁
sleep():指定的毫秒内让正在执行的线程休眠
currentThread():返回对当前正在执行的线程对象的引用
6、并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发:通过cpu调度算法,让用户看上去是同步执行。
悲观锁
synchronized:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。如CAS atomic;缺点是不能解决脏读问题,会出现ABA问题,添加版本号来避免此问题
二、线程间通信方式
1、管道:半双工,数据只能单向流动,有血缘关系的进程间流动
2、有名管道: 允许无血缘关系的进程间流动
3、信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。是一种同步手段
4、消息队列:由消息组成的链表,存放在内核中,并由消息队列标识符标识
5、信号: 用于通知接收进程某一事件已经发生。
6、共享内存
7、套接字socket
长连接:整个通讯过程,客户端和服务端只用一个Socket对象,长期保持Socket的连接
短连接:每次请求,都新建一个Socket,处理完一个请求就直接关闭掉Socket
三、线程间通信机制
有锁机制、信号量机制和信号机制
锁机制:
互斥锁: 提供了以排它方式阻止数据结构被并发修改的方法
读写锁:允许多个线程同时读共享数据,而对写操作互斥
条件变量:可以以原子阻塞进程,直到某个特定条件为真为止
信号量机制:包括无名线程信号量与有名线程信号量
四、线程安全的实现方法
1、互斥同步:临界区、互斥量、信号量
2、非阻塞同步: 硬件指令集
3、ReentrantLock:可重入锁
1)等待可中断:持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情
2)可实现公平锁:公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁;
3)锁可以绑定多个条件
4)默认非公平的通过带布尔值的构造函数要求使用公平锁
4、synchronized
重量级操作,状态切换需要耗费很多的处理器时间。确实必要才使用,虚拟机本身对它的优化:通知操作系统阻塞线程之前加入一段自旋等待过程,避免频繁的切入到核心状态之中。是非公平的。
5、如何确保线程安全
1)使用原子类
2)实现并发锁
3)使用volitale关键字
4)使用不变类和线程安全类
五、Web并发访问问题
1、Servlet线程安全问题:单例模式,存在线程安全问题
1)在Servlet中不要定义类变量或实例变量
2)为操作该变量的所有方法加synchronized修饰符,进行同步控制
2、Spring线程安全问题:优点是我们不用每次创建Controller, 减少了对象创建和垃圾收集的时间,缺点是并发访问时会出现安全问题。
1)避免在Controller中定义类变量和实例变量
2)在Controller中使用ThreadLocal变量
3)声明scope="prototype",每次创建新的Controller
3、SpringMVC线程安全问题
1)对操作共享变量的所有方法进行同步控制
2)同步共享变量如:Collections.synchronizedMap()
3)使用同步对象
六、线程池
1、为什么使用线程池
1)创建/销毁线程伴随着系统开销,过于频繁,影响处理效率
2)线程并发数量过多,抢占系统资源导致阻塞
3)对线程进行一些简单的管理(延时,定时循环等)
2、优点
1)降低资源消耗
2)提高响应速度
3)提高线程的可管理性
3、java.uitl.concurrent.ThreadPoolExecutor
1)corePoolSize:核心线程数最大值
2)maximunPoolSize:线程池最大线程数(核心线程 + 非核心线程)
3)keepAliveTime:对非核心线程没有任务执行时最多保持多久时间会终止
4)workQueue:一个阻塞队列,用来存储等待执行的任务
ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
LinkedBlockQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
SynchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务
5)threadFactory:线程工厂
6)handler:表示当拒绝处理任务时的策略
AbortPolicy:丢弃任务并抛出异常
DiscardPolicy:丢弃任务不抛出异常
DiscardOldestPolicy:丢弃队列最前面的任务,重新尝试执行任务
由调用线程处理该任务
7)策略
线程数量未达到corePoolSize,则新建一个核心线程执行任务
线程数量达到了corePoolSize,则进入队列等待
队列已满,新建非核心线程执行任务
队列已满,总线程达到了maximumPoolSize则抛出RejectedExcutionHandler异常
4、线程池分类
1)fixThreadPool:正规线程
有指定的线程数的线程池,有核心的线程,里面有固定的线程数量,响应的速度快
没有超时机制,队列大小没有限制,除非线程池关闭了核心线程才会回收
2)cacheThreadPool:缓存线程池
只有非核心线程,最大线程数很大有超时回收机制
没有考虑到系统的实际内存大小
3)singleThreadPool:单线程线程池
只有一个核心线程,不处理并发的操作,不会被回收
4)ScheduledThreadPool
有延迟执行和周期重复执行的线程池,核心线程池固定,非核心线程池不限,闲置时会回收。
5、可能出现的危险
死锁、资源不足、并发错误、线程泄漏、请求过载、
6、核心线程
核心线程默认情况下会一直存活在线程池中,即使这个核心线程啥也不干(闲置状态)。
线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corePoolSize,则新建的是非核心线程;allowCoreThreadTimeOut设置此项可以让核心线程闲置状态下回收
7、添加任务
ThreadPoolExecutor.execute(Runnable command)
8、线程池的关闭
shutdown():不会立即关闭,是等待所有任务缓存队列中的任务都执行完后才终止,但也不会接受新的线程
shutdownNow():立即终止线程池,尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务