死锁的条件:
1.互斥 资源只能一个线程持有
2.请求与保持 一个线程持有资源,然后请求另外一个资源,阻塞不释放
3.不可抢占 线程持有,只能主动释放
4.互相等待 A、B。。。多个线程相互等待
减少上下文切换 (Lmbench3 时长、vmstat 次数)
1.无锁并发 任务分段
2.CAS
3.使用最少线程 任务少,线程多,大多线程处于等待状态
4.协程 单线程实现多任务
避免死锁常见方法:
1.避免一个线程同时获取多个锁。
2.避免一个线程内在锁内占用多个资源,尽量保证每个锁占用一个
3.尝试使用定时锁lock.tryLock
4.对于数据库锁,加锁解锁必须在一个连接内
资源限制(将串行执行的部分变成并发执行,资源不足会变慢)
硬件:带宽,硬盘,使用集群 数据id%机器数
软件:数据库连接数,socket数 使用资源复用
根据不同的资源限制调整程序的并发度
synchronized实现同步的基础:
1.普通同步方法,锁实例对象
2.静态 Class对象
3.同步方法块,锁括号中对象 synchronized(对象)
在jvm中使用Monitor对象来实现,方法同步(也可用以下方法实现这里用另外
的一种方法)、
代码块同步(monitorenter,monitorexit)
线程通信:1.共享内存(隐式通信) 2.消息传递(之间发送消息,显式)
显式同步,需要指定互斥块 隐式同步发送必须在接受消息前
java使用共享内存模型
在没有 数据依赖性 的情况下,会进行指令重排
(读写、写读、写写)
当存在控制相关性的时候 也会进行指令重排
if(flag){
a = i*i;
}
先执行temp=i*i;(存入重排序缓冲) 然后判断flag,为真执行a=temp;
单线程中对控制依赖的操作重排序不会改变执行结果,多线程可能会!!
顺序一致性内存模型
1.一个线程所有操作必须按照程序的顺序进行
2.所有线程都能看到单一操作执行顺序,每个操作原子,且立刻对其他线程可见。
JMM(java memory model)通过同步程序来达到顺序一致性效果,尽可能为编译器和处理器优化便利。
JMM 不保证
1.单线程内操作按程序顺序执行
2.所有线程都能看到一致的操作执行顺序
3.对64bit的long double 写操作的无原子性 jdk5以后读为原子操作
volatile的内存语义
(可见性)
对被该修饰的变量,单个读和写可看做是使用同一个锁对单个读写进行同步
如果是多个volatile操作或者复合操作(volatile++)整体上不具有原子性
(单个volatile变量的读写的原子性)
读,,写,,写读,读写不具有原子性
(部分有序性)
操作1 普通读写 操作2 volatile写禁止,,其他的允许
当第二个操作是volatile写的时候,禁止重排序
当第一个操作是volatile读,禁止重排序
第一位volatile写,第二为volatile读写不允许排序 普通读写可以
-------------------------------------------------------------------------------------------
普通读写 volatile读
StoreStore屏障 禁止普通写与下一步重排 LoadLoad屏障 禁止后面的
volatile写 LoadStore屏障 读写和volatile重排
StoreLoad屏障 禁止上一步与后面可能的volatile读写重排 普通读写
----------------------------------------------------------------------------------------------
LoadLoad 确保Load1数据装载先于Load2以及所有后续装载指令
LoadStore 确保Load1数据装载先于Store一局所有后续存储指令
StoreStore 确保Store1数据对其他处理器可见(刷新到内存)先于Store2以及所有后续存储指令
StoreLoad 确保Store1数据对其他处理器可见(刷新到内存)先于Load2以及所有后续装载指令的装载。
锁的内存语义 除了让 临界区互斥执行 释放锁的线程向获取同一个锁的线程发送消息
1.线程A释放一个锁,实质上是A向接下来获取该锁的某线程发送消息
2.线程B获取一个锁,实质上是B获取了之前的某个线程发出的消息
3。A释放,B获取,即A通过主内存向B发送消息
AQS(AbstractQueueSynchronizer)
final域的重排序规则
以下不能重排序
1.构造函数内对一个final域的写入,与随后把该对象的引用赋值给一个引用变量
2.初次读一个final域的对象的引用,与随后初次读这个final域
final域的写重排序规则
1.JMM禁止编译器把final域的写重排到构造函数之外
2.编译器会在final写之后,构造函数return之前,插入一个StoreStore屏障禁止把final写重排到构造函数之
外
final域的读重排序规则
1.一个线程中初次读对象引用与初次读对该对象包含的final域,JMM禁止处理器重排序这两个操作(仅对CPU)
会在final域读操作之前插入一个LoadLoad 两个操作之间存在间接依赖关系,禁止少数CPU重排序这两个操作。
DoubleCheck和延迟初始化 减少创建类的开销增加了耗时
DoubleCheck
利用volatile 禁止 初始化对象与设置指向instance的空间重排序
基于类初始化 允许重排序,但是不被其他线程看到
private static class InstanceHolder{
public static Resource re = new Resource(); //会使用 Class对象初始化锁 和 state字段
} //其中状态有noInitialization initializing
public static Resource getResource(){ //initialized 三个状态 ,其他检测到字段wei
return InstanceHolder.re; //第二个,就释放锁,挂起等待唤醒
}
Daemon线程 在start之前设置 其线程中的finally块不一定执行
第4章
Thread.interrupted()对线程的中断方法进行复位
安全线程终止应使用 中断 或者 标识位
!!!仅仅通过flag 然后循环等待,会 难以保持及时性,难以 降低开销
通过一个object的wait(会释放锁) notify ,以及flag 循环等待 来对两个线程之间传递消息
ThreadA ThreadB 需要都获得 object的对象锁
notifyAll() 会把所有等待线程全移动到同步队列 被移动的线程状态由Waiting变为Blocked
static boolean flag = true;
static Object lock = new Object();
ThreadA
synchronized(lock){
while(flag){
lock.wait();
}
处理逻辑
}
ThreadB
synchronized(lock){
flag = false;
lock.notifyAll();
}
thread.join() join()会在thread死亡后返回当前线程
ThreadLocal 可以用于计算方法时间 set(obj) get(obj) 以一个ThreadLocal为键,任意对象为值的存储结构
线程等待超时模式
public synchronized Object get(long mills) throws InterruptedException{ //对当前对象加锁
long future = System.currentTimeMills()+mills; // 等待终止时间
long remaining = mills; // 剩余等待时间
while((result==null)&&remaining>0){ // 没有得到结果 且 仍存在等待时间 循环等待
wait(remaining); //等待
remaining = future - System.currentTimeMills(); // 等待后 剩余时间
}
return result; //返回结果
}