什么样的环境需要锁
1、多任务环境(多线程、分布式)
2、任务都需要对同一共享资源进行写操作
3、对资源的访问是互斥的(不能同时进行操作)
竞争锁的几个状态
1、竞争锁:任务通过竞争获得资源的锁
2、占有锁:任务对资源进行操作
3、任务阻塞:其他任务进行阻塞,等待占有锁的任务释放锁
4、释放锁:任务完成操作,释放锁
线程安全注意点
1、共享变量:如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量。
2、可见性:一个线程对共享变量值的修改,能够及时地被其他线程看到。
3、原子性: 一段指令像原子一样不可分割,在执行结束之前,其他线程不可打断。
锁类型
1、可重入锁(synchronized和ReentrantLock):在执行对象中所有同步方法不用再次获得锁
2、可中断锁(synchronized就不是可中断锁,而Lock是可中断锁):在等待获取锁过程中可中断
3、公平锁(ReentrantLock和ReentrantReadWriteLock): 按等待获取锁的线程的等待时间进行获取,等待时间长的具有优先获取锁权利
4、读写锁(ReadWriteLock和ReentrantReadWriteLock):对资源读取和写入的时候拆分为2部分处理,读的时候可以多线程一起读,写的时候必须同步地写
synchronized和Lock的共同点
1、协调多线程对共享对象、变量的访问
2、可重入,同一线程可以多次获得同一个锁
3、都保证了可见性和互斥性
synchronized和Lock的区别
类别 | synchronized | Lock |
---|---|---|
存在层次 | Java的关键字,在jvm层面上 | 通过接口实现 |
锁的释放 | 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 |
在finally中必须释放锁,不然容易造成线程死锁 |
锁的获取 | 1、假设A线程获得锁,B线程等待 2、如果A线程阻塞,B线程会一直等待 |
Lock有多个锁获取的方式,可以通过tryLock尝试获得锁,线程可以不用一直等待 |
锁状态 | 无法判断 | 可以判断 |
锁类型 | 可重入、不可中断、非公平 | 可重入、可判断、可公平,为处理锁提供了更高的灵活性 |
锁机制 | synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低 | Lock之所以能实现线程安全的锁,主要的核心是AQS,AQS提供了一个FIFO队列,可以看做是一个用来实现锁以及其他需要同步功能的框架。AQS的使用依靠继承来完成,子类通过继承自AQS并实现所需的方法来管理同步状态 |