第一章Java并发挑战主要有: 上下文切换问题: 死锁 资源限制 定义: 实现线程访问共享变量时,为了确保共享变量能够准确和一致的更新。 这就很好的解决了以上所说的问题。 Java中每一个对象都可以所作锁使用(毕竟有对象头就可以),主要加三种表现形式: 底层实现: 锁的分类: 注意: 锁只能升级不能降级。 处理机制: //再用CAS写个单例,不过没有太大实用价值,还是建议用doublecheck、饿//汉啊,当然最推荐的还是枚举的写法 //阔以感受一下,虽然最后得到的同一对象,不过可不止实例化了一个对象
解决思路:
1.减少线程数量
2.增长线程内的有效利用率(就是线程运行时间和上下文切换时间)
解决思路:
1.避免一个线程同时获得多个锁
2.避免一个线程在锁内同时占用多个资源。
3.尝试使用定时锁(lock.tryLock(timeout))
4.对数据库锁,保证操作由一个连接完成
第二章:volatile
为何需要: 由于在默认的情况下,CPU发现一个操作数是可以进行缓存的便将其缓存下来 (缓冲行填充),若下次再访问该操作数所在地址时,则不通过内存访问,而是直接访问缓冲行中的值 (缓存命中),进行操作,再判断写入的地址是否已经存于缓存行,若是则将值再写到缓冲行 (写命中)。
这样在多线程的场景下,就会影响问题,线程读到的时之前的数据,各做各的也没有达到目标。
如何解决:
这时变需要使用volatile变量了,它添加到某变量上时,就保证了CPU只能从内存读值且缓存行无效。
它会给原来的指令追加一条带lock前缀的指令,这个指令保证了两件事:
volatile的优化:
文中提到的LinkedTransfarQueue类 是将数据填充为64字节(或者说68字节),由于某些缓冲行为64字节,所以无法将队列头部指针和尾部指针同时缓存,使得头尾的修改无法互相锁定。
适用的条件:
synchronized
对象头见此文相关部分
JVM中基于进入和退出Moniter对象来实现同步,方法同步和代码块同步的实现原理可不一样,不过都是使用monitorenter 和monitorexit指令,monitorenter指令在编译后插入到同步代码块的开始位置,monitorexit则插入到结束或者异常位置,当一个monitor被持有后,这段代码块便处于锁定状态。
1.偏向锁:
是一种特殊的“无锁”状态,当一个线程访问同步代码块时,会在对象头和栈帧中的锁记录里储存锁偏向的线程ID,只需简单的测试下对象头的Mark Word是否存储着指向当前线程的偏向锁,若是则成功获得锁,若不是,则看是否设置偏向锁标志,若是,改为本线程ID,若不是,则用CAS竞争锁。
**偏向锁的撤销:**很明显出现竞争时,偏向锁便不再适用了,等待一个没有执行的字节码的时间点(全局安全点),首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着,死了,锁的对象头设置为无锁,活着,要么偏向其他线程,要么恢复到无锁,要么标记该对象不适用于偏向锁。
关闭偏向锁:-XX:BiasedLockingStartupDelay=0 //由于偏向锁默认程序启动几秒后启动,若需要可以取消延迟 -XX:UseBiasedLocking=flase //如果程序中所有锁通常处于竞争状态可以关闭偏向锁 

2.轻量级锁
加锁:
首先将锁对象头的MarkWord复制到栈帧的锁记录中(Displaced Mark Word),再将MarkWord替换为指向锁记录的指针,若成功,线程获得锁,若失败则说明有竞争,以自旋CAS方式获取锁。若超过三个线程竞争该锁直接膨胀为重量级锁
解锁:
将锁记录中存的MarkWord写回对象头中,若成功,表示当前没有竞争,若失败,膨胀为重量级锁(因为在必然存在竞争时,无用的自旋会让CPU做无用功)。


3.重量级锁
互斥锁,在某个线程持有锁时,其他尝试竞争锁的线程都会被阻塞,当锁被释放时,再唤醒其他线程,重新竞争锁。
锁
优点
缺点
适用场景
偏向锁
无需额外消耗
存在竞争时产生额外的锁撤销消耗
只有一个线程访问同步块时
轻量锁
不会阻塞提高相应速度
若存在长时间竞争,自旋消耗CPU
追求响应速度,同步块执行快
重量锁
不使用自旋
线程阻塞,响应慢
追求吞吐量(竞争多),同步块执行时间长
原子性

public class CASTest { private static AtomicInteger atomicInteger = new AtomicInteger(); private static int i; public static void main(String[] args) throws InterruptedException { List<Thread> list = new ArrayList<>(); for(int i=0;i<1000;i++){ list.add(new Thread(()->{ for(int j =0;j<1000;j++) { safeAdd(); unsafeAdd(); } })); } for(int i =0;i<1000;i++){ list.get(i).start(); } for(Thread i:list){ i.join(); } System.out.println("safe:"+atomicInteger.get()); System.out.println("unsafe:"+i); } static void safeAdd(){//保证原子性 while(true){ int count = atomicInteger.get(); if(atomicInteger.compareAndSet(count,count+1)){ break; } } } static void unsafeAdd(){//瞎几把加 i++; } } public class CASSingle { private static AtomicReference<CASSingle>instance= new AtomicReference<>(); private CASSingle(){} public static CASSingle getCasSingle() { while(true){ CASSingle casSingle = instance.get(); if(casSingle!=null){ return casSingle; } casSingle = new CASSingle(); System.out.println(Thread.currentThread().getName()+"新建了一个对象"); if(instance.compareAndSet(null,casSingle)){ return casSingle; } } }

CAS的三大问题:
1.ABA问题:A->B->A表面上没有修改,其实改过了,解决方法是追加版本号(jdk的Atomic包下的AtomicStampedReference来解决,先检查预期再检查版本号)
2.循环时间长开销大
3.只能保证一个共享变量原子操作,用原子引用解决呀,封装成一个类。
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算
官方软件产品操作指南 (170)