Synchronized和Lock接口

关于synchronized字段,无论该关键字是修饰方法仍是修饰同步代码块,synchronzed拿到的都是对象。
当synchronized修饰的是方法时,synchronized所拿到的是调用该方法的对象的锁,通常状况下都是this的锁;
当synchronized()修饰的同步代码块时,synchronized拿到的是指定对象的锁。
 
扩展:
当synchronized修饰的静态方法时,因为静态方法不包含this,属于类层次的方法,因此,synchronized拿到的是这个方法所属Class对象的锁。
java中的对象每一个只含有一个锁,经过synchronized来获取对象的锁。
 
注意:当方法异常退出时,其对象锁能够有JVM进行释放。
占有锁的线程释放锁时通常是如下三种状况:
一、 占有锁的线程执行完了代码块,而后释放对锁的占有;
二、 占有锁的线程发生了异常,此时JVM会让线程自动释放锁;
三、 占有锁的线程调用了wait()方法,从而进入了WAITING状态须要释放锁。
 
探究-->对象都含有什么锁?
对象锁:又称独占排它锁,经过名字咱们即可知道,在多线程程序中,一旦一个线程到达了共享区(即synchronized修饰的区域)。那么该线程将拥有该共享区的对象锁,其余线程想要进入,只能等到该线程释放了锁才可进入。对应于非静态方法和非静态代码块。
类锁:方法或代码块所在类的类对象的锁。对应于静态方法和静态代码块。
 
 
Lock接口
体系类图:
方法介绍:
Lock:是Lock接口中使用最多的获取锁的方法,若是锁被其余线程占用就等待。 因为Lock接口是基于JDK层面的,因此,锁的释放动做必须手动进行。不像synchronized是基于Java语言的特性,属于JVM层面,锁的获取和释放动做都由JVM自动进行,对开发者是透明的。
使用方式:
Lock lock = ...; lock.lock();
try{    
   //处理任务
}catch(Exception ex){
}finally{
    lock.unlock();   //释放锁
}
 
tryLock:表示用来尝试获取锁,若是获取成功即返回TRUE,若是锁被其余线程占用,则返回FALSE;该方法会当即返回结果,不会一直处于等待状态。
 
tryLock(long time,TimeUnit unit):与tryLock相似,区别在于这个方法在拿不到锁的状况下会等待一个时间time, 在时间期限以内若是还拿不到锁,就返回FALSE;同时能够相应中断。若是在一开始或者等待期间得到了锁,则返回true。
问题:若是在开始没有拿到锁,那么代码块会进入到等待状态么?
猜测:我以为会进入等待状态,不过在等待的这段时间内,会存在一个响应机制来监测别的线程是否释放了锁,若是释放了锁,则直接有该线程获取锁,该方法返回TRUE;若是没有释放,那么该方法将再也不享有响应机制的提醒,并返回FALSE。
使用方式:
Lock lock = ...;
if(lock.tryLock()) {
     try{
         //处理任务     
   }catch(Exception ex){
     }finally{
         lock.unlock();   //释放锁     
  }
}else {   
  //若是不能获取锁,则直接作其余事情
}
lockInterruptibly:当经过这个方法尝试获取锁时,若是线程正在等待获取锁,那么这个线程可以响应中断,即被本身或者其余线程中断线程的等待状态。例如,当两个线程同时经过lock.lockInterruptibly()方法获取锁时,假如线程A获取了锁,那么线程B就只能进入等待状态,那么对线程B调用ThreadB.interrupt()可以中断线程B的等待状态。
 
注意:lockInterruptibly方法必须放在try块中或者在调用lockInterruptibly的方法外声明抛出InterruptedException,推荐使用后者。
 
使用方式:
public void method() throws InterruptedException {     lock.lockInterruptibly();     try {       //.....     }     finally {         lock.unlock();     }  }
疑问:释放掉线程的等待状态有什么用处?
相比于synchronized,等待状态的线程没法响应中断。提升了代码的灵活性,当不想持续的等待下去时,响应中断去作其他的事情,更具灵活性。
 
ReadWriteLock
该锁提高了读操做的效率,不过要注意的是, 若是有一个线程已经占用了读锁,则此时其余线程若是要申请写锁,则申请写锁的线程会一直等待释放读锁。若是有一个线程已经占用了写锁,则此时其余线程若是申请写锁或者读锁,则申请的线程也会一直等待释放写锁。
 
 
Lock和synchronized的选择:
一、 Lock是一个接口,属于JDK层面的实现;而synchronized属于Java语言的特性,其实现有JVM来控制。
二、 synchronized在发生异常时,会自动释放掉锁,故不会发生死锁现(此时的死锁通常是代码逻辑引发的);而Lock必须在finally中主动unlock锁,不然就会出现死锁。
三、 Lock可以响应中断,让等待状态的线程中止等待;而synchronized不行。
四、 经过Lock能够知道线程是否成功得到了锁,而synchronized不行。
五、 Lock提升了多线程下对读操做的效率。
 
扩展
可重入锁:synchronized和ReentrantLock都是可重入锁。当一个线程执行到某个synchronized方法时,好比说method1,而在method1中会调用另一个synchronized方法method2,此时线程没必要从新去申请锁,而是能够直接执行方法method2。
 
可中断锁:经过上面的例子,咱们能够得知Lock是可中断锁,而synchronized不是。
 
公平锁:尽可能以请求的顺序来获取锁,同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最早请求的线程)会得到该锁。synchronized是非公平锁,而ReentrantLock和ReentReadWriteLock默认状况下是非公平锁,可是能够设置成公平锁
ReentrantLock lock = new ReentrantLock(true);
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
设置为TRUE即为公平锁,为FALSE或者无参数为非公平锁。
 
读写锁:读写锁将对临界资源的访问分红了两个锁,一个读锁和一个写锁。即ReadWriteLock接口及其实现ReentrantReadWriteLock。
相关文章
相关标签/搜索