一个复杂的流程中,如果耗时比较长操作不影响最终的结果,那么我们可以把它剥离出来单独执行:
两种方案:
1,多线程,把耗时操作方法另外一个线程单独执行
2,使用消息列对分隔两个操作
多线程:
进程:计算机上可以单独执行的程序。
线程:执行代码的地方。
一个进程至少包含一个线程,这个线程叫做主线程。
代码在线程中串行执行(按照顺序依次执行),在多线程间并行执行(所有线程一起执行)。
单个线程同一时间只能执行一个操作,如果想要同时执行多个操作,必须使用多线程,把多个操作给分配到不同的线程去执行。
Java中创建线程的方式:
1,自定义类继承Thread类,重写run()方法
2,实现Runable接口,实现run()方法
3,实现Callable接口,实现run()方法
都需要在run()方法中编写在另外一个线程中执行的代码
线程的状态转换:
创建->就绪->运行->就绪->运行->死亡
创建->就绪->运行->阻塞->就绪->运行->死亡
多线程不安全:
当多个线程修改同一资源(数据)的时候,会产生线程数据不同步的问题。
需要通过加锁的方式实现线程同步,来保证线程安全:
1,使用synchronized关键字对代码加锁,加锁后的代码在多线程间同步串行进行。
2,使用synchronized关键字修饰方法,此方法变为同步方法
3,手动加锁解锁:ReentrantLock(独享锁)lock() unlock()
注意加解锁的顺序,避免形成死锁
线程不安全需要加锁,线程安全的不需要加锁(自身已经加锁)
Java中的线程安全类:
StringBuffer,Vector,ConcurrentHashMap
Java中的线程不安全类:
StringBuilder,ArrayList,HashMap
线程池:
频繁的创建和销毁线程非常消耗资源,拖累程序的运行效率,可以使用线程池回收待销毁的线程重新使用,避免频繁的创建。
使用 ThreadLocalPool 代替 Executors 创建线程池:
根据 workQueue 创建不同类型的线程池
SynchronousQueue:
有空闲线程则执行任务,否则拒绝任务
ArrayBlockingQueue:
有空闲线程则执行任务,否则任务暂存到 queue 中,
如果超出 queue 容量上限,则创建线程执行任务,
直到线程数达到上限,再有新任务则拒绝
LinkedBlockingQueue:
有空闲线程则执行任务,否则任务暂存到 queue 中,queue 容量无上限
PriorityBlockingQueue:
有空闲线程则执行任务,否则任务暂存到 queue 中,queue 容量无上限,可以设置任务的优先级
合理的线程池数量:
如果是 IO 密集型(数据读写操作)应用,则线程池大小设置为 2N + 1;
如果是 CPU 密集型(数据计算操作)应用,则线程池大小设置为 N + 1;
N 代表 CPU 的核数。
单例模式(Singleton):
单例指的是某个类在程序的整个生命周期内只能存在一个实例/对象。
好处:
1. 实现数据在整个程序内的共享和统一管理
2. 避免实例对象的重复创建,减少创建实例的系统开销
实现:
1. 禁止通过 new + 构造方法创建对象
重写默认的构造方法,修改访问修饰符为 private
private Singleton() {}
2. 提供获取对象的唯一方式
public static Singleton singleton = new Singleton();
3. 修改为懒汉模式
1. 删除给静态变量赋值代码,并修改为 private
private static Singleton singleton;
2. 添加 getInstance() 作为获取对象的唯一方式
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
4. 保证线程安全
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
spring 的 IoC 容器中,默认创建的对象都是单例模式,并且程序已启动就创建
dao,service 和 controller 都是只有一个对象在反复的使用
创建的对象除了单例还有另外 3 种模式:
prototype:多例,每次使用的时候都创建一个新的对象
session:在同一个 session 内使用同一个对象
request:在同一个 request 内使用同一个对象
使用 @Scope 注解可以设置创建对象的模式
spring 中的对象都是线程不安全的,修改属性值的时候注意同步问题
ThreadLocal:
把存储的数据和线程绑定起来,当前线程存储的值只有当前线程能用
static final ThreadLocal<T> sThreadLocal = new ThreadLocal<T>();
sThreadLocal.set()
sThreadLocal.get()