java各种锁机制及多线程

157 阅读6分钟

一切的源头在于榨取cpu,因为cpu跑的比内存快;执行一条指令,取决于内存存取的速度。于是在cpu和主存之间加了一块高速缓存cache,cpu执行在cache之间执行,执行完写回主存。

线程

进程:资源调度的基本单位
线程:cpu执行的基本单位

java锁

cpu内核数和线程数的关系?
一个内核对应一个线程,后来有超线程技术,可以模拟出多个
cpu时间片轮转机制 又叫RR,可以有多个,但有限制。 代价上下文切换消耗性能

常见方法介绍
位于Thread类的

  • stop() 废弃 导致资源得不到正常释放
  • interrupt() 中断线程 不会立即停止,只是打了一个终端标志,线程可不理会。可配合isInterrupted()使用
  • interrupted() 判断是否中断并清空标志位,且是静态方法;
  • isInterrupted() 判断是否中断

使用boolean变量去关闭线程,,,可能会不及时 因为遇到sleep,wait
抛出中断异常,所以使用中断来关闭线程推荐

  • yield() 当前线程让出cpu执行权,进入就绪状态;极有可能还是选中自己;不会让锁
  • sleep() 不会让锁
  • join() a线程 执行b.join() a要等b执行完才执行 可让线程先后执行
  • setDaemon() 设置为守护线程;守护线程在所有非守护线程结束后自动结束;并且守护线程中的finlly不一定执行

线程调度方式:

  • 协作式:一个线程执行完,通知下一个线程执行
  • 抢占式:由系统分配执行时间,线程切换不由线程决定

用来保证线程之间安全执行;前提是多个线程之间存在共享变量的访问;如果多线程之间没有共享变量的访问,不管是同步还是串行都没有安全问题;

synchronized

synchronized 就是语言特性 隐式锁
一般来说,都算重量级锁。jdk1.6后有了各种锁优化,性能还好。
原理:synchronized代码块或者方法,编译后会添加监视器进入和监视器结束指令。。。。来获取对象的监视器

对象锁
每个锁对象都有2个队列,一个就绪,一个阻塞;一个线程被唤醒会进入就绪;反之一个线程被wait就会进入阻塞;

  • 同步块 锁普通变量 锁静态变量 锁this:锁对象实例
  • 同步方法 普通方法:锁对象实例 静态方法:锁类对象

lock

Lock 是接口
具体实现有:ReentrantLock、ReadLock、WriteLock

一种显式锁,手动申请锁 释放锁;

显示锁标准范式:

lock.lock();
try{
     // dddd
}finally{
     lock.unlock();
}

常见锁概念

公平锁&非公平锁
一种分类;获取锁的时候是不是按照先来后到的原则,是就是公平锁,否则不是

可重入锁&不可重入锁
一种分类;对于同一个线程,可以重复获得同一把锁;synchronized是可重入锁

重量级锁&轻量级锁
一种分类;相对而言,synchronized偏重量级,轻量级锁有volatile,。。。。

乐观锁&悲观锁
一种分类;乐观锁就是每次都觉得没有人跟自己抢锁,直接操作。悲观锁,总有线程跟我抢,每次都去抢锁

显示锁&非显示锁
一种分类;显示锁就是手动申请锁 释放锁;与之不同的就是synchronized

独占锁&共享锁
读写锁,读共享 写独占 读写互斥

虚拟机锁优化

针对一些特殊的情况,采取一定的手段降低性能消耗

自旋锁 为了降低线程阻塞(阻塞就会挂起和唤醒线程,会转入内核态完成)的压力;并且部分共享数据的锁定状态持续很短的时间,这时阻塞就会不值得。让线程等待,采取一个忙循环;看持有锁的线程是否很快释放;如果超过一定次数(默认10),会用传统的方式挂起线程;
1.6默认开启,并引入自适应自旋;

锁消除 即时编译阶段,虚拟机行为;
虚拟机通过逃逸分析,去除不可能存在共享资源竞争的锁;比如stringbuffer在append时,每个applend都加了synchronized,当用在局部变量时,虚拟机会将锁退化掉

偏向锁 线程第一次获取锁,没有其他线程获取锁,该线程第二次获取跳过获取锁。如果有其他线程获取就会升级为重量锁

锁粗化 对于连续加锁同一个对象的代码,可以放大加锁范围,使加一次锁,避免频繁获取锁、释放锁所带来的的性能消耗
volatile 可见性 禁止重排序 不保证原子性 一写多读

threadlocal spring的事务(把数据连接存进去)
thread的一个成员变量ThreadLocal.ThreadLocalMap,每一个threadlocal作为key放进map中 threadlocal弱引用
最佳实践 在使用后 在删除掉;不能保存共享的变量,比如static修饰的变量

阻塞同步
通过线程阻塞和唤醒的同步

非阻塞同步
先进行操作,如果没有其他线程争用共享数据,则操作成功;反之,有了冲突,采用其他补偿措施。

异常释放锁

cas (compare and swap)比较交换
需要处理器指令支持cas指令 保证比较交换是原子性
保存(内存地址,旧值,)
ABA问题:带版本号解决

Lock 与 synchronized 区别?
synchronized 关键字 lock 接口对象 消耗不一样
synchronized 非公平 lock 都有

AQS

设计模式:模板方法

lock 使用的AQS

线程通信

等待/通知机制

常见方法介绍
位于Object类的

wait()
当前线程获取到锁之后,该方法使当前线程等待,执行后立即释放锁,直到其他线程通知这个锁对象上等待的线程。
当其他线程执行通知后,这个锁对象上等待的线程需要重新获取这个锁,获取到锁的线程在wait()方法后可继续执行;

     synchronized (obj) {
     while (<condition does not hold>)
          obj.wait();
     ... // Perform action appropriate to condition
     }

wait(timeout)
当timeout=0,相当于wait();同wait(),不同的是在超时时间过后,会自动移出等待队列,然后竞争获得锁以得到执行;

notify() & notifyAll()
通知对象锁上等待的其他线程;执行后不立即释放锁;区别是notify()只通知一个;notifyAll()通知所有的

     synchronized (obj) {
          obj.notify();
     ... // Perform action appropriate to condition
     }

共享变量
通过轮询这个变量,取决于轮询的间隔,会消费性能或者响应不及时。