避免死锁的方法
在进行并发编程时,程序的执行速度受限于计算机硬件资源或软件资源
串行代码变成并发执行时,如果受限于资源,仍然串行执行,不仅不会快反而会更慢,增加了上下文切换和资源调度的时间。
例如:使用多线程在办公网并发地下载和处理数据时,导致cpu利用率达到100%,几个小时都不能运行完成任务,后来修改成单线程,一个小时就执行完成了。
将不同的资源限制调整程序的并发度
如果一个字段被声明成了volatile,java线程内存模型确保所有线程看到这个变量的值是一致的。
Java代码
instance = new Singleton() ;//instance是volatile变量
转成汇编代码
0x01a3de1d:movb $0x0,0x1104800(%esi);0x01a3de24:lock add1 $0x0,(%esp);
Lock前缀的指令在多核处理器引发两件事情
过程
volatile两条实现原则
jdk7 并发包中新增了一个队列集合类Linked-TransferQueue,在使用volatile时,用一种追加字节的方式来优化队列出队和入队的性能。
LinkedTransferQueue结构
追加字节能优化队列出队和入队性能:内部类PaddedAtomicReference相对于父类AtomicReference多了将共享变量追加到64字节(15个变量+父类valule变量)
为什么提高并发编程效率:有些处理器的l1,l2或l3缓存的高速缓存行使64字节宽。追加到64字节填满高速缓冲区的缓存行,避免了头结点和为节点加载到同一个缓存行,使头尾节点在修改时不会互相锁定。
不能使用追加64场景
synchronized实现同步的基础:Java中的每个对象都可以作为锁
Monitor指令
synchronized用的锁是存在java对象头里的,如果对象是数组类型,虚拟机用3个字宽存储对象头,如果对象是非数组类型,用2字宽存储对象头。
Mark World:存储对象的hashCode,分代年龄和锁标记位
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oshe1Pci-1692516244748)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20230820120519714.png)]
Class Metadata Address:存储到对象类型数据的指针
Array length:数组的长度(如果当前对象是数组)
多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得。
**偏向锁撤销:**等到竞争出现才释放锁的机制,当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。
关闭偏向锁
java6和java7中默认启用,但在应用程序启动几秒之后才激活
关闭延迟:-XX:BiasedLockingStartupDelay=0
关闭偏向锁:-XX:-UseBiasedLocking=false
加锁
解锁
轻量级锁膨胀流程
当锁处于重量级,其他线程试图获取锁时,会被阻塞,当持有锁的线程释放锁后会唤醒这些线程,被唤醒的线程会进行新一轮的夺锁之争。
优缺点对比
基于对缓存加锁或总线加锁方式实现多处理器之间的原子操作
使用处理器提供的Lock#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占共享内存。
总线锁把cpu和内存之间的通信锁住了,其他处理器不能操作其他内存地址的数据,总线锁定的开销比较大。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
private AtomicInteger atomicInteger = new AtomicInteger(0);
private int i = 0;
public static void main(String[] args) {
final Counter cas = new Counter();
List<Thread> ts = new ArrayList<Thread>(600);
long start = System.currentTimeMillis();
for (int j = 0; j < 100; j++) {
Thread thread = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10000; i++) {
cas.count();
cas.safeCount();
}
}
});
ts.add(thread);
}
for (Thread t : ts) {
t.start();
}
//等待所有线程执行完成
for (Thread t : ts) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(cas.i);
System.out.println(cas.atomicInteger.get());
System.out.println(System.currentTimeMillis()-start);
}
private void safeCount(){
for (;;){
int i = atomicInteger.get();
boolean b = atomicInteger.compareAndSet(i, ++i);
if(b){
break;
}
}
}
private void count(){
i++;
}
}
java1.5,jdk提供了一些类支持原子操作:AtomicBoolean,AtomicInteger和AtomicLong
java并发包中有些并发框架使用了自旋cas方式实现原子操作。
参考:Java并发编程的艺术