java ReentranLock锁
1、效果和synchronized一样,都可以同步执行,lock方法获得锁,unlock方法释放锁
使用示例:
package com.test; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MyService { private Lock lock = new ReentrantLock(); public void methodA() { try { lock.lock(); System.out.println("methodA begin ThreadName = " + Thread.currentThread().getName() + "time=" + System.currentTimeMillis()); Thread.sleep(3000); System.out.println("methodA end ThreadName = " + Thread.currentThread().getName() + "time=" + System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void methodB() { try { lock.lock(); System.out.println("methodB begin ThreadName = " + Thread.currentThread().getName() + "time=" + System.currentTimeMillis()); Thread.sleep(3000); System.out.println("methodB end ThreadName = " + Thread.currentThread().getName() + "time=" + System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
package com.test; public class Run { public static void main(String[] args) { MyService myService = new MyService(); Thread t1 = new Thread(new Runnable() { @Override public void run() { myService.methodA(); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { myService.methodB(); } }); t1.start(); t2.start(); } }
结果: methodA begin ThreadName = Thread-0time=1527821296840 methodA end ThreadName = Thread-0time=1527821299841 methodB begin ThreadName = Thread-1time=1527821299841 methodB end ThreadName = Thread-1time=1527821302841
注意:必须要在finally块里调用lock.unlock() 释放锁.
2、使用Condition实现等待/通知:
awati() 与 signal() 方法:
package com.test; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MyService { private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void testWait() { try { lock.lock(); System.out.println("wait"); condition.await(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void testSignal() { try { lock.lock(); condition.signal(); } finally { lock.unlock(); } } }
- 通过Condition对象来使线程wait,必须先执行lock.lock()获得锁。
- Condition对象的signal()方法可以唤醒线程.
- Condition的awati()方法和Object 中的wati()方法等效.
- Condition的signal()方法和Object 中的notify()方法等效.
- Condition的signalAll()方法和Object中的notifyAll()方法等效。
3、公平锁和非公平锁:
公平锁标识线程获取锁的顺序是按照线程加锁的顺序来分配的。即先来先得的FIFO先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的。
Lock lock=new ReentrantLock(true);//公平锁 Lock lock=new ReentrantLock(false);//非公平锁
4、ReentrantLock 类的方法:
- int getHoldCount() 的作用是查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。
- int getQueueLength()的所用是返回正在等待获取此锁的线程估计数 。比如有5个线程,1个线程首先执行await()方法,那么调用此方法的返回值是4.
- int getWaitQueueLength(Condition condition)的所用是返回等待与此锁定相关的给定条件Condition的线程估计数,比如5个线程都执行了Condition的await()方法 ,那么返回值就是5.
- boolean hasQueuedThreads()的作用是查询是否有线程正在等待获取此锁.
- boolean hasWaiters(Condition condition)的作用是查询是否有线程正在等待与此锁定有关的condition条件。
- boolean isFair()的作用是判断是不是公平锁.
- boolean isHeldByCurrentThread()的作用是查询当前线程是否保持此锁。
- boolean isLocked()的作用是查询此锁是否由任意线程保持。
- void lockInterruptibly()的作用是:如果当前线程未被中断,则获取锁定,如果已经被中断则出现异常。
- boolean tryLock()的作用是仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定。
- boolean tryLock(long timeout, TimeUnit unit) 的作用是,如果锁定在给定等待时间内没有被领一个线程保持,且当前线程未被中断,则获取该锁定。
5、ReentrantReadWriteLock 类,读写锁:
类ReentrantLock 具有完全互斥排他的效果,即同一时间只有一个线程在执行lock()方法后面的任务。
读写锁表示也有两个锁,一个是读操作相关的锁,也成为共享锁;另一个是写操作相关的锁,也就排它锁。也就是多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。在没有线程进行写操作时,进行读取操作的多个线程都可以获取读锁,而进行写入操作的线程只有在获取写锁后才能进行写入操作。即多个线程可以同时进行读操作,但同一时刻只有一个线程可以进行写操作。
- 读读共享
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public void read() { try { try { lock.readLock().lock(); System.out.println("获取读锁:" + System.currentTimeMillis()); Thread.sleep(1000); } finally { lock.readLock().unlock(); } } catch (Exception e) { e.printStackTrace(); } }
- 写写互斥
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public void write() { try { try { lock.writeLock().lock(); System.out.println("获取写锁:" + System.currentTimeMillis()); Thread.sleep(1000); } finally { lock.writeLock().unlock(); } } catch (Exception e) { e.printStackTrace(); } }
- 读写互斥
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public void read() { try { try { lock.writeLock().lock(); System.out.println("获取读锁:" + System.currentTimeMillis()); Thread.sleep(1000); } finally { lock.writeLock().unlock(); } } catch (Exception e) { e.printStackTrace(); } } public void write() { try { try { lock.writeLock().lock(); System.out.println("获取写锁:" + System.currentTimeMillis()); Thread.sleep(1000); } finally { lock.writeLock().unlock(); } } catch (Exception e) { e.printStackTrace(); } }
lock 与 lockInterruptibly比较区别在于:
lock 优先考虑获取锁,待获取锁成功后,才响应中断。
lockInterruptibly 优先考虑响应中断,而不是响应锁的普通获取或重入获取
synchronized 和 lock 的用法区别:
- synchronized 是托管给JVM执行的。而Lock是Java写的控制锁的代码。
- synchronized 原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,会导致效率很低。
- Lock用的是乐观锁方式。每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试。直到成功为止。
- ReentrantLock必须在finally中释放锁,而synchronized不需要。
- ReentrantLock提供了可轮询的锁请求,他可以尝试的去取得锁,如果取得成功则继续处理,取得不成功,可以等下次运行的时候处理,所以不容易产生死锁。而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以更容易产生死锁。
- synchronized的话,锁的范围是整个方法或synchronized块部分;而Lock因为是方法调用,可以跨方法,灵活性更大。
使用ReentrantLock的场景:
- 某个线程在等待一个锁的控制权的这段时间需要中断。
- 需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程.
- 具有公平锁功能,每个到来的线程都将排队等候.