关于synchronize与lock的区别

参考文献:https://www.cnblogs.com/cloudblogs/p/6440160.html

一、synchronize修饰不同代码都是锁住了什么?
大家都知道synchronize可以修饰属性、代码块,方法、类,但是修饰不同的代码锁住的内容是不同的。
1、修饰非静态属性和方法时,拿到的是调用这个方法或者属性的对象(this)的锁
2、synchronize()修饰代码块时,拿到的是指定对象的锁
3、修饰类、静态方法、静态代码块时,由于没有this指针,因此拿到的是类锁,也就是该类的class对象
!!!注意:一个对象只有一个锁
 
二、关于synchronize
由于synchronize是由JVM实现的,因此当加锁代码出现异常时,对象锁可以由JVM释放,包含以下三种情况:
1、 占有锁的线程执行完了代码块,然后释放对锁的占有;
2、 占有锁的线程发生了异常,此时JVM会让线程自动释放锁;
3、 占有锁的线程调用了wait()方法,从而进入了WAITING状态需要释放锁。
 
三、关于Lock

 

 由于Lock是由JDK实现的,所以不像synchronize锁的获取和释放都是由JVM控制的,Lock的获取和释放都需要手动进行,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生

1、lock() 获取锁与释放锁 ,如果锁已被其他线程获取,则进行等待。

1 Lock lock = ...; lock.lock();
2 try{    
3    //处理任务
4 }catch(Exception ex){
5 }finally{
6      lock.unlock();   //释放锁 
7 }

 

2、tryLock() 获取锁时有返回值可以得知获取锁操作是否成功 

 1 Lock lock = ...; 
 2 if(lock.tryLock()) {
 3       try{ 
 4          //处理任务     
 5    }catch(Exception ex){
 6      }finally{
 7           lock.unlock();   //释放锁      
 8   }  
 9 }else {   
10   //如果不能获取锁,则直接做其他事情 
11 }

如果获取成功则返回True,如果锁被其他线程占用则返回FALSE,该方法会立即返回结果不会让线程一直处于等待状态

 

3、tryLock(long time,TimeUnit unit) 与tryLock() 类似,但是与tryLock立即返回结果不同,该方法在拿不到锁的情况下回等待time时间,如果在限定时间内还是拿不到锁就返回FALSE,如果在一开始或者等待时间内拿到锁则返回TRUE。

 

4、lockInterruptibly() 

通过这个方法尝试获取锁时,如果线程正在等待获取锁,则该线程可以响应Thread.interrupt()中断。synchronize对于没有获得锁处于等待状态的线程无法响应中断。

1 public void method() throws InterruptedException {     
2     lock.lockInterruptibly();    
3     try {        //.....     }     
4     finally { lock.unlock(); }   
5 }

lockInterruptibly方法必须放在try块中或者在调用lockInterruptibly的方法外声明抛出InterruptedException,推荐使用后者。

 

5、readWriteLock()

该锁提升了读操作的效率,不过要注意的是,如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程也会一直等待释放写锁。

 

四、Lock和synchronized的选择:
1、 Lock是一个接口,属于JDK层面的实现;而synchronized属于Java语言的特性,其实现有JVM来控制(代码执行完毕,出现异常,wait时JVM会主动释放锁)。
2、 synchronized在发生异常时,会自动释放掉锁,故不会发生死锁现(此时的死锁一般是代码逻辑引起的);而Lock必须在finally中主动unlock锁,否则就会出现死锁。
3、 Lock能够响应中断,让等待状态的线程停止等待;而synchronized不行。
4、 通过Lock可以知道线程是否成功获得了锁,而synchronized不行。
5、 Lock提高了多线程下对读操作的效率。
 
五、扩展
1、可重入锁:synchronized和ReentrantLock都是可重入锁。当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2。
 
2、可中断锁:等待获得锁的等待过程是否可以中断。通过上面的例子,我们可以得知Lock是可中断锁,而synchronized不是。
 
3、公平锁:尽量以请求的顺序来获取锁,同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该锁。synchronized是非公平锁,而ReentrantLock和ReentReadWriteLock默认情况下是非公平锁,但是可以设置成公平锁。
ReentrantLock lock = new ReentrantLock(true);
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
设置为TRUE即为公平锁,为FALSE或者无参数为非公平锁。
 
4、读写锁:读写锁将对临界资源的访问分成了两个锁,一个读锁和一个写锁。增加读写灵活性。即ReadWriteLock接口及其实现ReentrantReadWriteLock。
posted @ 2019-09-13 20:16  simpleDi  阅读(8699)  评论(0编辑  收藏  举报