JAVA进阶--锁
Java 中的锁分类
①乐观锁/悲观锁
乐观锁: 就是没有加锁的实现. AtomicInteger(原子类)中的实现就是不加锁的,通过自旋比较实现 (CAS)
悲观锁: 就是加锁的实现,认为不加锁是会出问题的 ,ReentrantLock和synchronized都是悲观锁
②可重入锁
ReentrantLock和synchronized都是可重入锁
可重入锁又名递归锁, 指的是一个线程在外层方法获得锁时,可以直接进入到内层的加锁的方法中.
public class Demo {public synchronized void setA(){System.out.println("setA");setB();}private synchronized void setB() {System.out.println("setB");}public static void main(String[] args) {new Demo().setA();}}锁对象会记录线程id,当进入到setB时,判断线程id相同则可以进入,两个方法共用同一把锁,当线程进入setA获得锁时,可直接进入到setB方法中,这就是可重入锁
如果不是可重入锁的话,setB不会 被当前线程执行,造成死锁。
③自旋锁
指的是对synchronized获得锁的一种描述(特点), 线程在获得锁时,是自旋的不断尝试去获得锁
④公平锁/非公平锁
公平锁: 就是排队获得锁,有先来后到 ReentrantLock 既可以是公平锁也可以是非公平锁
非公平锁: 就是抢锁,谁抢到谁执行, 有可能后来的线程先抢到锁 synchronized ReentrantLock
⑤读写锁
ReentrantReadWriteLock
特点: 读读不互斥, 读写互斥, 写写互斥
适合读(查询)多,写少的场景, 提高读的效率
private int data;//共享数据private ReadWriteLock rwl=new ReentrantReadWriteLock();//写数据public void set(int data){rwl.writeLock().lock();//取到锁try{System.out.println(Thread.currentThread().getName()+"准备写入数据");this.data=data;System.out.println(Thread.currentThread().getName()+"写入"+this.data);}finally {rwl.writeLock().unlock();//释放锁}}//读数据public void get(){rwl.readLock().lock();//取到锁try{System.out.println(Thread.currentThread().getName()+"准备读取数据");System.out.println(Thread.currentThread().getName()+"读取"+this.data);}finally {rwl.writeLock().unlock();//释放锁}}⑥共享锁和独占锁
独占锁: synchronized ReentrantLock都是独占锁,就是有我没他,一次只能有一个线程执行.
共享锁: 一个锁可以被多个线程持有, 读写锁中的读锁就是共享锁
synchronized锁的实现
jdk1.7之后,对synchronized锁进行了优化(jdk7之前synchronized锁没有状态,都是自旋的获取锁),
jdk7之后为synchronized锁设计了不同的状态.
synchronized修饰方法 非静态方法锁对象是this 静态方法是类的Class对象
synchronized修饰代码块
使用synchronized锁的时候,必须为锁提供一个同步锁对象的,此对象就是用来记录锁状态的
对象中有一个区域叫对象头,对象头中有一块区域叫mark word,记录对象运行时的一些数据,如锁状态,哈希值,GC分代年龄,当前线程id.

锁状态
无锁状态: 没有线程进入到同步代码块就是无锁状态
偏向锁状态: 只有一个线程访问同步代码块时,同步锁中记录线程id,下次线程访问时,可以快速的获得锁.
轻量级锁状态: 当线程数量大于1个之后,锁状态由偏向锁升级为轻量级锁, 线程不会阻塞,以自旋方式获得锁,
提高获取锁的效率.
重量级锁状态: 当锁状态为轻量锁时,如果线程自旋到一定次数还获取不到锁,那么锁会升级为重量级锁,让获取不到锁的线程进入到阻塞状态,等待操作系统调度.
AQS
AbstractQueuedSynchronizer 抽象同步队列, 并发包下面很多类的底层实现都会用到
内部有一个int类的变量state,用来记录有没有线程使用
内部会构建一个队列,用来存储没有获得锁的线程




ReentrantLock锁实现
ReentrantLock 基于 AQS的,
ReentrantLock 可以实现公平锁和非公平锁
内部结构:
ReentrantLock 类内部总共存在Sync、NonfairSync、FairSync 三个类,并且三个内部类是紧密相关的, NonfairSync 与 FairSync 类 继 承 自 Sync 类 , Sync 类 继 承 自AbstractQueuedSynchronizer 抽象类。

公平和非公平的区别:


JUC常用
Java5.0在java.util.concurrent包中提供了多种并发容器类来改进同步容器的性能。
1.ConcurrentHashMap
ConcurrentHashMap是一个线程安全的map,加锁的方式与Hashtable不同
Hashtable直接在方法上加锁,一次只能有一个线程进入方法操作.
ConcurrentHashMap不是给方法加的锁,给每个哈希表中的位置加锁

2.CopyOnWriteArrayList
ArraayList 是线程不安全的,在高并发情况下可能会出现问题,Vector是线 程安全的。
3.CopyOnWriteArraySet
CopyOnWriteArraySet 的实现基于CopyOnWriteArrayList,不能存储重复数 据
4.辅助类CountDownLatch
CountDownLatch 允许一个线程等待其他线程各自执行完毕后再执行。底层实 现实现是通AQS来完成的.创建CountDownLatch对象时指定一个初始值是线 程的数量。每当一个线程执行完毕后,AQS内部的state就-1,当state的值为 0 时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了