DougLea 并发设计大师
为什么需要线程
单核到多核,达到并行计算,线程是轻量级,成本低,效率高
合理利用多核cpu资源,提升对计算机资源的利用
java中如何使用线程
- 继承Thread类,重写run方法
- 实现Runnable接口
- Callable带返回值的线程,返回值用Future接收
- ThreadPool线程池
实际应用
线程合理利用cpu资源,提高程序的吞吐量
比较多的实际是线程池,一般不会new一个线程来使用,线程有风险
使用new Thread造成资源不可控
如何去应用线程池?
对账,通过线程进行跑批
BIO模型优化
如何改造程序实现异步化处理?
并发基础
生命周期
线程共有六种状态
线程使用目的,提高程序的性能
java中的线程状态 6种
线程启动,终止
interrupt
锁(保护线程安全)
synchonic 修饰方法,修饰代码块,修饰实例
以上方法锁的范围不同。作用范围不同,锁的范围由对象生命周期决定的
锁的范围,为什么锁的范围会有影响,
互斥锁的本质:共享资源
锁,对象锁
这是jvm源码,这里保存对象头
下面这个就是对象头
轻量级锁,重量级锁,偏向锁,
加锁会带来性能开销,
偏向锁,轻量级锁,重量级锁
锁的升级
jdk1.6之前是加重量级锁,会导致性能下降,之后又优化,这里涉及锁的升级。这个是由jvm层面实现的,我们不用管。
针对不同情况进行优化,提高效率。
首先偏向锁,没有获取就会升级到轻量级锁,然后重量级锁,阻塞线程
所以优化就是,在线程阻塞前,抢占锁,轻量级或者偏向锁
让线程不阻塞,也可以抢占到锁。在不加锁情况解决线程安全问题
偏向锁(默认关闭,线程没有竞争时候提升性能,真是情况没有必要开启偏向锁,所以一般就是无锁,轻量级锁,重量级锁)
首先访问同步代码块,看对象头中是有存储了线程id,(上面对象头表中有)如果没有存储,会通过cas操作(CAS,乐观锁,保证原子性,比较并替换,比较预期数据和原始内存中数据是否一致)将其替换掉。当前线程会存储标记,这是在没有竞争的情况下,用偏向锁 如上图 1表示偏向锁,01表示锁的标记,ThreadId会有值。下图总共32位
如果现在有另一个线程访问,cas操作会失败,会撤销原来获得的偏向锁,暂停原来的线程,解锁,将对象头中的线程id设置为空,恢复到无锁状态,恢复线程。
如果原来线程正在运行,无法撤销,会升级锁为轻量级锁,然后以轻量级锁竞争锁对象
线程栈有Lock record,对象头Object复制到其中,owner指向锁对象
轻量级锁,栈中存储对象头,通过cas操作,指向占中的指针。当线程2进入,失败,重试机制,自旋锁,多次cas操作(线程获得锁和释放锁时间短),但是也不能一直重试,自适应自旋,会根据上一次抢占时间长短觉定这一次时间多少(底层实现),相比于线程阻塞,挂起的性能好,所以用自旋。如果在指定自旋(自适应自旋)此时还没有获得锁,自旋失败,修改为重量级锁,没有获得锁的线程阻塞,等待唤醒
重量级锁,队列中排队被阻塞的线程。
唤醒,进行新一轮的抢占
非公平锁,允许插队,syncholized
如何优化,存在竞争就会升级到重量级锁
生产者和消费者。两个不同的线程
线程通信(wait/notify)等待唤醒
wait作用,释放当前同步锁,实现线程阻塞
notify 唤醒被阻塞