java Lock
一、Lock锁:
Lock是一个接口,Lock需要手动的释放锁,Lock锁可以知道是否成功的获取锁。
二、Lock接口中的方法
获取锁:Lock() 、tryLock()、tyrLock(long time,timeUnit unit)、lockInterruptibly()
释放锁:unlock()
四种获取锁方法的区别:
1.Lock()这种方法是最常用的方法,
由于在前面讲到Lock锁必须手动释放锁,并且在发生异常时不会自动释放锁。因此一般来说,使用Lock必须在try{}catch(){} 中。而释放锁的过程放在finally中,以保证锁即使在异常的情况下也能被正常的释放,防止死锁的发生。
Lock lock = ...; lock.lock(); try{ //处理任务 }catch(Exception ex){ }finally{ lock.unlock(); //释放锁 }
2.tryLock()方法是有返回值的方法,此方法可用来判断是否获取了锁,如果获取了锁,返回值为true,如果获取失败(即锁已被其它线程占用),则返回值为false。也就是说这个方法不会一直原地等待,无论是否拿到锁都会立即返回。
Lock lock = ...; if(lock.tryLock()) { try{ //处理任务 }catch(Exception ex){ }finally{ lock.unlock(); //释放锁 } }else { //如果不能获取锁,则直接做其他事情 }
3.tryLock(long time,timeUnit time)方法和tryLock() 方法类似,不同的是此方法在拿不到锁会等待一段时间,如果在期限的时间内还拿不到锁,就不再等待,而是返回false。如果一开始或期限时间内拿到锁,就返回true。
4.lockInterruptibly()方法比较特殊,当用西方法获取锁的时候,如果线程处在没有获取到锁,而正在等待获取锁,那么这个线程是可以被中断,即中断线程的等待状态。也就是说,当两个线程同时通过lock.lockInterruptibly()向获取锁时,加入线程A获取了锁,线程B只能等待,那么对线程B调用B.lockInterruptibly() 是中断线程B的等待过程。
注意:当一个线程获取了锁之后是不会被interrupt()方法打断的,interrupt()可以打断正在阻塞的线程,不可以打断正在执行的进程。
因此当通过lockInterruptibly()方法获取锁时,如果获取不到,只有进行等待的情况下,是可以响应中断的。而如果使用synchronized修饰的话,当一个线程处于正在等待某个锁的状态没事无法中断的,只能一直等待下去。
三、ReentrantLock
意思是可重入锁,是Lock接口的实现类。
来看一个实例
1.lock()方法
public class TestLock { public static void main(String[] args) { Thread1 thread=new Thread1(); Thread thread1=new Thread(thread); Thread thread2=new Thread(thread); thread1.start(); thread2.start(); } } class Thread1 implements Runnable{ private Lock lock=new ReentrantLock(); public void run(){ lock.lock(); try{ for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName()+"i "+i); } }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } }
运行结果:
2.tryLock() 方法的使用
public class TestLock { public static void main(String[] args) { Thread1 thread = new Thread1(); Thread thread1 = new Thread(thread); Thread thread2 = new Thread(thread); thread1.start(); thread2.start(); } } class Thread1 implements Runnable { //公用一把锁 private Lock lock = new ReentrantLock(); public void run() { if (lock.tryLock()) { try { System.out.println(Thread.currentThread().getName()+"获取了锁"); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "i " + i); } } catch (Exception e) { e.printStackTrace(); } finally { System.out.println(Thread.currentThread().getName()+"释放了锁"); lock.unlock(); } }else{ System.out.println(Thread.currentThread().getName()+"获取锁失败"); } } }
运行结果:
四、ReadWriteLock
ReadWriteLock是一个接口,在里面只定义了两个方法:
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(); }
readLock()方法是用来获取读锁,writelock()是用来获取写锁,也就是将文件的读写操作分开,分成2个锁操作来分配给线程,从而使得多个线程可以同时进行读操锁。
五、ReentrantReadWriteLock
ReentrantReadWriteLock实现了ReadWriteLock,ReentrantReadWriteLock提供了很多方法,不过最重要的两个方法:readLock()和WriteLock()来获取读锁和写锁。
public class MyReentrantReadWriteLock { private ReentrantReadWriteLock rrwl=new ReentrantReadWriteLock(); public static void main(String[] args) { MyReentrantReadWriteLock m=new MyReentrantReadWriteLock(); new Thread(){ public void run(){ m.get(Thread.currentThread()); } }.start(); new Thread(){ public void run(){ m.get(Thread.currentThread()); } }.start(); } public void get(Thread thread){ rrwl.readLock().lock(); try{ System.out.println(thread.getName()+"得到锁"); long start=System.currentTimeMillis(); while(System.currentTimeMillis()-start<=1){ System.out.println(thread.getName()+"正在读取文件"); } }finally { System.out.println(thread.getName()+"释放锁"); rrwl.readLock().unlock(); } } }
运行结果:
六、与synchronized的区别
1)在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降很多,但是ReetrantLock的性能能维持常态。
2)Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;
3)Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。