Synchronized和Lock
Synchronized | Lock | |
---|---|---|
存在层次上 | 是Java关键字,在jvm层面上处理 | 是一个接口 |
锁的释放 | 1.已经获取锁的线程执行完同步代码,释放锁. 2.线程执行发生异常,jvm会让线程释放锁 | 在finally中必须手动释放锁,不然容易造成线程死锁 |
锁的获取 | 假设A线程获得锁,B线程等待,如果A线程阻塞,B线程会一直等待 | 有多个锁的获取方式,可以尝试获取锁,线程不用一直等待,用tryLock判断有没有锁 |
死锁产生 | 异常会自动释放锁,不会有死锁 | 异常不释放锁,需要手动unlock释放锁,可能会引起死锁 |
锁的状态 | 无法判断状态 | 可以判断状态 |
锁的类型 | 可重入,不可中断,非公平锁 | 可重入,可判断,可公平(两者皆可) |
性能 | 少量同步 | 大量同步 |
调度 | 使用object对象本身的wait、notify、notifyAll调度机制 | 可以使用Condition进行线程之间的调度 |
用法 | 需要在同步对象中加入此控制,可加在方法上,特定的代码块中,括号中表示需要锁得对象 | 一般使用ReentrantLock类作为锁,在加锁和解锁处要使用lock()和unlock()进行指出,所以一般会在finally块中写unlock()以防死锁 |
底层实现 | 底层使用指令码方式来控制锁的,映射成字节码指令就是增加来两个指令:monitorenter和monitorexit。当线程执行遇到monitorenter指令时会尝试获取内置锁,如果获取锁则锁计数器+1,如果没有获取锁则阻塞;当遇到monitorexit指令时锁计数器-1,如果计数器为0则释放锁 | 底层是CAS乐观锁,依赖AbstractQueuedSynchronizer类,把所有的请求线程构成一个CLH队列。而对该队列的操作均通过Lock-Free(CAS)操作 |
synchronized的缺陷,Java Lock如何处理?
synchronized的缺陷
效率低:锁的释放情况很少,只有代码执行完毕或者异常结束才会释放试图获取所得时候不能设置超时,不能中断一个正在使用锁的线程,相对而言,Lock可以中断和设置超时。
不够灵活:加锁和释放锁的时机单一,每个锁仅有一个单一的条件(某个对象),相对而言,读写锁更加灵活
无法知道是否成功获得锁:无法判断状态
Lock的处理
主要方法:
1.lock()加锁
2.unlock()解锁
3.tryLock() 尝试获取锁,返回一个Boolean值
4.tryLock()尝试获取锁,可设置超市时间
Synchronized只有锁只与一个条件(是否获取锁)相关联,不灵活,后来Condition与Lock的结合解决了这个问题。
多线程竞争一个锁时,其余未得到锁的线程只能不停的尝试获得锁,而不能中断。高并发的情况下会导致性能下降。ReentrantLock的lockInterruptibIy()方法可以优先考虑响应中断。一个线程等待时间过长,它可以中断自己,然后ReentrantLock响应这个中断,不再让这个线程继续等待。有了这个机制,使用ReentrantLock时就不会像Synchronized那样产生死锁了。
学习笔记欢迎指正