Synchronized和Lock
Synchronized关键字
1.作用
保证同一时刻最多只有1个线程执行 被Synchronized修饰的方法 / 代码
其他线程 必须等待当前线程执行完该方法 / 代码块后才能执行该方法 / 代码块
2.修饰
2.1修饰方法
//对象锁
//写法1
public synchronized void method()
{
// todo
}
//写法2,写法2其实是同步代码块的写法,但在这里也是相当于修饰了方法
public void method()
{
synchronized(this) {
// todo
}
}
2.2修饰静态方法
//类锁
public synchronized static void method()
{
// todo
}
2.3修饰类
//类锁
class ClassName {
public void method() {
synchronized(ClassName.class) {
// todo
}
}
}
2.4修饰代码块
//对象锁
//写法一
synchronized(this) {
//todo
}
'
//写法二
Object obj =new Object();
synchronized(obj) {
//todo
}
2.5底层实现
synchronized 同步代码块的实现使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。
synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法。
2.6可重入锁
- AQS通过控制status状态来判断锁的状态,对于非可重入锁状态不是0则去阻塞;对于可重入锁如果是0则执行,非0则判断当前线程是否是获取到这个锁的线程,是的话把status状态+1,释放的时候,只有status为0,才将锁释放。
3.总结
- 当synchronized用来修饰静态方法或者类时,将会使得这个类的所有对象都是共享一把类锁,导致线程阻塞,所以这种写法一定要规避
- 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,它取得的是对象锁;如果synchronized作用的对象是一个静态方法或一个类,该类所有对象持有同一把锁。
- 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
- 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
4.特别注意
Synchronized修饰方法时存在缺陷:若修饰1个大的方法,将会大大影响效率
5.特点
- 保证原子性、可见性、有序性
- 可重入
- 重量级
Lock关键字
Lock和synchronized有以下几点不同
- 1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
- 2)synchronized会自动释放线程占有的锁,而Lock需要主动通过unLock()去释放锁,否则很可能造成死锁现象。
- 3)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
- 4)Lock可以提高多个线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况。
Lock接口
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
Lock lock = ...;
lock.lock();
try{
//处理任务
}catch(Exception ex){
}finally{
lock.unlock(); //释放锁
}
----------
Lock lock = ...;
if(lock.tryLock()) {
try{
//处理任务
}catch(Exception ex){
}finally{
lock.unlock(); //释放锁
}
}else {
//如果不能获取锁,则直接做其他事情
}
--------------
public void method() throws InterruptedException {
//调用它时需要主动抛出异常,如果获得锁就执行,如果锁已经被其它线程得到,那就抛InterruptedException异常
lock.lockInterruptibly();
try {
//.....
}
finally {
lock.unlock();
}
}
ReentrantLock
ReentrantLock,意思是“可重入锁”。ReentrantLock是唯一实现了Lock接口的类
ReadWriteLock
ReadWriteLock也是一个接口
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*/
Lock readLock();
/**
* Returns the lock used for writing.
*/
Lock writeLock();
}
锁的相关概念
- 可重入锁
可以重复获取已获得的锁 - 可中断锁 可以中断自己获取锁的过程
- 公平锁
尽量以请求锁的顺序来获取锁 synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。 而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。 - 读写锁 读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。