锁在多线程编程中有很重要的做用,synchronized比较常见也很经常使用,可是Lock提供了更普遍的锁操做,处理多线程同步的问题也更加优雅和灵活,Java从Java SE 5以后在并发包中提供Lock接口。编程
1、Lock和synchronized的区别和各自的特色多线程
一、类型不一样
Lock是一个接口,是JDK层面的实现;synchronized是Java的关键字,是JVM层面的实现,是Java的内置特性;这也形成了在锁管理上的不一样。
二、释放锁的方式
Lock是在编码中进行锁的操做,须要主动调用接口中的方法释放锁,因此使用Lock时须要配合try模块,在finally中释放锁,否则一旦发生异常极可能形成死锁现象;synchronized因为是JVM层面的实现,获取锁和释放锁的操做是不可见的,当发生异常时,锁的释放由JVM实现。
三、获取锁的过程
Lock接口中提供的方法,让获取锁的过程更加灵活,编程人员能够方便的在获取锁的过程当中进行多种操做,好比尝试获取锁、设置获取锁的超时时间、中断等待获取锁的线程等,应该说让锁的操做变得“丰富多彩”起来;synchronized是没法这么灵活的对锁进行操做的。
四、效率
基于读写锁接口的扩展,Lock能够提升多个线程进行读操做的效率,在大并发量的状况下,效率的提升会尤为明显。并发
2、Lock应用场景举例编码
一、解决获取锁的等待问题
若是占有锁的线程A因为各类缘由致使阻塞而没有释放锁,此时其余线程B也须要得到该锁。synchronized的机制是让B持续等待,若是A一直没有释放锁,那么B将一直等待,这会很大程度影响执行的效率;而Lock中提供了中断线程等待的方法,也提供了带有超时时间的获取锁的方法,后面会讲到这些方法。
二、读写锁的分离
咱们知道,多线程仅仅是执行读操做的话是没有冲突问题的,于是在读操做时的锁不必是独占的。synchronized实现同步就会致使在读操做时只能有一个线程得到锁,其余线程只能等待锁的释放。Lock中的ReentrantReadWriteLock很好的解决了这个问题。
三、其余锁的操做
如获知当前线程是否成功得到锁等,synchronized是作不到的。spa
3、Lock接口中的方法线程
先看一下Lock的源码,它是一个接口:接口
public interface Lock {资源
void lock();get
void lockInterruptibly() throws InterruptedException;同步
boolean tryLock();
boolean tryLock(long var1, TimeUnit var3) throws InterruptedException;
void unlock();
Condition newCondition();
}
在介绍每一个方法以前,先说明两个注意事项:
一、Lock的实例通常定义为成员变量,若是定义为局部变量,每一个线程都会保存一个本身的副本,那么在获取锁的操做时,实际每一个线程获取的是不一样的锁,没法造成同步互斥访问。
二、获取锁的操做要放在try模块以外,缘由是若是放在try模块内的话,当获取锁的操做发生异常会调用finally中的代码释放锁,而此时可能并无获取锁,就会抛出异常。
接下来看一下每一个方法的做用:
1、lock()
特色:发生异常不自动释放锁;若是没有获取到锁会等待;
代码示例:
private Lock lock = new ......; //建立锁
public void getLock() {
lock.lock(); //得到锁
try {
System.out.print("业务处理"); //任务处理
} catch (Exception e) {
} finally {
lock.unlock(); //释放锁
}
}
2、tryLock()
特色:带有boolean型返回值;不管是否成功获取锁会当即返回不进行等待;
private Lock lock = new ......; //建立锁
public void getLock() {
if(lock.tryLock()) {
try {
System.out.print("业务处理"); //任务处理
} catch (Exception e) {
} finally {
lock.unlock(); //释放锁
}
} else {
System.out.print("获取锁失败");
}
}
3、tryLock(long time, TimeUnit unit)
特色:能够设置获取锁的等待时间,如tryLock(4, TimeUnit.SECONDS)等待4秒;能够在等待过程当中相应中断;
示例代码略,参考tryLock()代码,要注意异常的处理。
4、lockInterruptibly()
特色:当一个使用该方法获取锁的线程没有获取到锁处于阻塞等待状态时,能够调用线程的interrupt()方法中断等待。
示例代码略。
Lock接口有一个实现类ReentrantLock,便可重入锁,除实现Lock接口的方法外,还提供了很是丰富的其余的锁操做方法,如公平锁和非公平锁。
·
4、读写锁ReadWriteLock
ReadWriteLock是一个接口,接口代码:
·
public interface ReadWriteLock {
Lock readLock(); //读锁
Lock writeLock(); //写锁
}
里面只定义了两个方法,一个获取读锁,一个得到写锁,将资源的锁分开为两个,从而使得资源能够在多线程情境下并发读操做。
ReentrantReadWriteLock类实现了ReadWriteLock接口,并提供了更多方法,最主要的仍是获取读写锁的方法。
读写锁代码示例:
public class LockTest {
private ReadWriteLock lock = new ReentrantReadWriteLock(); //建立锁
public static void main(String[] args) {
final LockTest lockTest = new LockTest();
new Thread("A") {
public void run() {
lockTest.getLock(Thread.currentThread());
}
}.start();
new Thread("B") {
public void run() {
lockTest.getLock(Thread.currentThread());
}
}.start();
}
public void getLock(Thread thread) {
lock.readLock().lock(); //得到读锁
try {
System.out.println("线程" + thread.getName() + "得到读锁");
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime <= 1) {
System.out.println("线程" + thread.getName() + "进行读操做......");
}
} catch (Exception e) {
} finally {
lock.readLock().unlock();
System.out.println("线程" + thread.getName() + "释放读锁");
}
}
}
结果:
5、几种锁的概念介绍
1、可重入锁
具有可重入性的锁,即为可重入锁,好比synchronized和ReentrantLock都是。锁基于线程分配,当某个线程得到了锁去执行某个方法,方法中若是再次须要获取锁资源时,当前线程能够直接得到锁,没必要从新申请。
2、可中断锁
能够响应中断的锁。synchronized不是可中断锁,可是Lock是可中断锁。
3、公平锁
尽可能按照请求锁的顺序得到锁。synchronized是非公平锁,ReentrantLock和ReentrantReadWriteLock提供了设置公平锁的方法,不过默认为非公平锁。非公平锁可能致使某些线程永远获取不到锁。
4、读写锁 将对资源的锁分为两个,一个读锁和一个写锁,使得多个线程之间的读操做不会发生冲突能够并行,这极大的提升了读的效率。不过读锁和写锁、写锁和写锁之间都是互斥的。