在java5以后引入了另外一种方式实现同步,就是Lock
synchronized锁的缺陷
如果一个代码块被synchronized关键字进行修饰了,当一个线程获取了对应的锁,并且执行该代码块的时候,其他线程只能一直等待,直到获取锁的线程释放掉锁,释放锁有两种情况:
1:获取锁的线程执行完了该代码块,然后会自动释放锁
2:执行线程发生了异常,此时JVM会自定释放掉线程的锁
那么如果这个获取锁的线程由于要等待IO或者其他原因(调用了sleep方法),但是又没有释放锁,其他线程只能等待,很影响程序的执行效率
如果利用sysnchronized关键字修饰同步方法,如果一个线程在进行读的操作,那么其他线程即使是读,也都是悲观锁的实现,很影响程序的执行效率,因此就需要一种机制来使得多个线程都只是进行读操作,线性之间不会发生冲突,通过Lock就可以实现,此外通过Lock可以知道有没有获取到锁,这在sysnchronized中是无法办到的
总结:
1)Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;
2)Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。
Lock
Lock是一个接口,源码如下:
package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
/**
*
* @see ReentrantLock
* @see Condition
* @see ReadWriteLock
*
* @since 1.5
* @author Doug Lea
*/
public interface Lock {
/**
* 用来获取锁,如果锁已被其他线程获取,则进行等待。
* 必须主动去释放锁,并且在发生异常时,不会自动释放锁。
* 一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,
* 以保证锁一定被被释放,防止死锁的发生。
*/
void lock();
/**
* 通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待
* 状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程
* A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的
* 等待过程。
* 由于lockInterruptibly()的声明中抛出了异常,所以lock.lockInterruptibly()必须放在try
* 块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。
*/
void lockInterruptibly() throws InterruptedException;
/**
* 用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),
* 则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
*/
boolean tryLock();
/**
* 该方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。
* 如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
*/
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
/**
* 释放锁
*/
void unlock();
/**
* 获取Condition类,Condition是Java提供了来实现等待/通知的类,Condition类还提供比
* wait/notify更丰富的功能,Condition对象是由lock对象所创建的。但是同一个锁可以创建多个
* Condition的对象,即创建多个对象监视器。这样的好处就是可以指定唤醒线程。notify唤醒的线
* 程是随机唤醒一个。
*/
Condition newCondition();
}
子父类继承/实现关系:
ReentrantLock(可重入锁)
ReentrantLock是一个可重入且独占式的锁,它具有与使用synchronized监视器锁相同的基本行为和语义,但与synchronized关键字相比,它更灵活、更强大,增加了轮询、超时、中断等高级功能。ReentrantLock,顾名思义,它是支持可重入锁的锁,是一种递归无阻塞的同步机制。除此之外,该锁还支持获取锁时的公平和非公平选择。
ReentrantLock内方法及抽象类:
ReentrantLock的类图如下:
ReentrantLock的使用
lock()方法的使用:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample1 {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
final LockExample1 example = new LockExample1();
new Thread(){
public void run(){
example.getLockOrUnLock(Thread.currentThread());
}
}.start();
new Thread(){
public void run(){
example.getLockOrUnLock(Thread.currentThread());
}
}.start();
}
public void getLockOrUnLock(Thread currentThread) {
lock.lock();
try {
System.out.println(currentThread.getName()+"获得了锁");
} catch (Exception e) {
e.printStackTrace();
}finally{
System.out.println(currentThread.getName()+"释放了锁");
lock.unlock();
}
}
}
运行结果:
Thread-0获得了锁
Thread-0释放了锁
Thread-1获得了锁
Thread-1释放了锁
tryLock()方法的使用:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
final LockExample example = new LockExample();
new Thread(){
public void run(){
example.tryLockOrUnLock(Thread.currentThread());
}
}.start();
new Thread(){
public void run(){
example.tryLockOrUnLock(Thread.currentThread());
}
}.start();
}
public void tryLockOrUnLock(Thread currentThread) {
if(lock.tryLock()){
try {
System.out.println(currentThread.getName()+"获得了锁");
} catch (Exception e) {
e.printStackTrace();
}finally{
System.out.println(currentThread.getName()+"释放了锁");
lock.unlock();
}
}else{
System.out.println("获取锁失败");
}
}
}
运行结果:
Thread-1获取锁失败
Thread-0获得了锁
Thread-0释放了锁
lockInterruptibly()方法的使用:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//测试类
public class LockExample2 {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
LockExample2 example = new LockExample2();
MyThread myThread = new MyThread(example);
MyThread myThread2 = new MyThread(example);
myThread.start();
myThread2.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myThread2.interrupt();
}
public void getLockOrUnLock(Thread currentThread) throws InterruptedException{
lock.lockInterruptibly(); //注意,如果需要正确中断等待锁的线程,必须将获取锁放在外面,然后将InterruptedException抛出
try {
System.out.println(currentThread.getName()+"获得了锁");
long startTime = System.currentTimeMillis();
for(;;){
if(System.currentTimeMillis() - startTime > Integer.MAX_VALUE){
break;
//
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
System.out.println(currentThread.getName()+"执行finally");
lock.unlock();
System.out.println(currentThread.getName()+"释放了锁");
}
}
}
//线程类
public class MyThread extends Thread {
private LockExample2 lockExample2 = null;
public MyThread(LockExample2 lockExample2) {
this.lockExample2 = lockExample2;
}
public void run(){
try {
lockExample2.getLockOrUnLock(Thread.currentThread());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "被中断");
e.printStackTrace();
}
}
}
运行结果:
Thread-0获得了锁
Thread-1被中断
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at com.zyf.eflying.lock.LockExample2.getLockOrUnLock(LockExample2.java:23)
at com.zyf.eflying.lock.MyThread.run(MyThread.java:13)
ReadWriteLock
是一个接口,源码如下:
package java.util.concurrent.locks;
/**
* @see ReentrantReadWriteLock
* @see Lock
* @see ReentrantLock
* @desc ReadWriteLock管理一组锁,一个是读锁,一个是写锁
* @since 1.5
* @author Doug Lea
*/
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
* 返回一个读锁
* @return the lock used for reading
*/
Lock readLock();
/**
* Returns the lock used for writing.
* 返回一个写锁
* @return the lock used for writing
*/
Lock writeLock();
}
子父类继承关系:
补充中...