1.指令重排
在多线程中,while (!ready),在执行的时候可能会变成
if(!ready) {
while(true) {
}
}
导致其他线程更改了ready的值也没用。
变量加上volatile,volatile使用了内存栅栏,内存栅栏中大多数操作不能被重排序。
指令重排也可能导致两条代码执行顺序颠倒。
Thread.yield() ///线程放弃CPU时间片,重新竞争,可能别人拿到,也可能还是自己拿到。
2.volatile
volatile能保证可见性和禁止指令重排。DCL(双重检查加锁)的变量记得用volatile修饰,如果初始化执行顺序为1-3-2,则可能有问题。
初始化过程:
1-分配内存。2-初始化对象。3-地址赋值给变量。
3.发布对象
对象的发布需要在初始化完成后,否则可能存在不一致状态。
4.对象的同步
保存在volatile修饰的域中。
保存在final修饰的域中。
保存在由锁保护的域中。读取和修改都用同一个锁同步。
5.ExecutorService
shutdown() 拒绝新的任务,并处理完正在执行和队列中的任务。
shutdownNow() 拒绝新的任务,结束正在执行的任务。 返回未执行的任务
awaitTermination()
接受新任务,把等待的任务和执行中的都处理完再关闭,可以传入超时时间。返回是否关闭成功。
isShudDown() 执行了shutdown()或shutdownNow()后返回true。
isTerminated() shutdown()或shutdownNow()执行完毕后返回true。
6.关闭钩子--JVM关闭时运行
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
System.out.println("运行关闭钩子");
}
}));
7.ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
基准线程数量,最大线程数量,线程空闲回收时间,时间单位,堵塞队列,线程工程,拒绝策略。
invokeAny(List<Callable> list) //获取最快执行完的结果,其他的中断执行。
invokeAll(List<Callable> list) //等待所有都执行完。获取结果集
继承可以使用:
beforeExecute(Thread t, Runnable t) //任务运行前执行
afterExecute(Thread t, Runnable t) //任务运行前执行
terminated() //线程池销毁的时候调用
execute() //没有返回值,运行时异常会打印到控制台
submit() //返回future,运行时异常不会打印到控制台,线程死了get返回null
8.上下文切换
jvm和操作系统中消耗CPU时钟周期越多,留给应用程序的就越少,但上下文切换并不只是包含jvm和操作系统的开销。当一个新线程切进来,它所需数据可能不在当前cpu缓存,过多的上下文切换会导致性能下降。
对大多数处理器,上下文切换开销相当于5000-10000个时钟周期,也就是几微秒。
9.放弃CPU使用权(可能导致上下文切换)
sleep //自己进入堵塞状态,让出CPU
yield //自己进入就绪状态,优先让其他线程获得CPU使用权(根据虚拟机策略)。
xxx.join //放弃当前线程的执行,先让xxx线程执行完,再自己执行。
10.减少锁的竞争
减小锁的范围,把不需要同步的代码移出锁的范围,耗时操作不要再同步代码中使用。
减小锁的粒度,不同变量使用不同的锁。
使用分段锁,集合可以使用分段锁,几个元素使用一个锁。下标%锁个数表示使用的锁。
避免热点域,例如在map中维护一个size来统计数量,那么每次操作都要同步size,那么这个size就是热点域,可以每个分段锁维护一个size,总数量是每个分段锁的size总和。
使用读写锁代替独占锁,读锁可重入,写锁独占。
谨慎使用对象池,通常同步开销比对象分配开销大。
11.原子变量(乐观锁-CAS)
一共有12个原子变量,可分为四组:标量类,更新器,数组类,复合变量类。
标量类:AtomicXXX
AtomicReference<T>则对应普通的对象引用,底层使用的compareAndSwapObject实现CAS,比较的是两个对象的地址是否相等。