参考文献: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的获取和释放都需要手动进行,但正是由于这样才更加灵活一些。
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。
原文地址:https://www.cnblogs.com/simpleDi/p/11517552.html