Lock的使用---ReentrantLock
ReentrantLock类的作用如同Synchronized,旨在实现线程间的同步。
一、初识ReentrantLock
MyService.java
/*
* ReentrantLock实现同步
* lock.lock()持有锁
* lock.unlock():一个线程执行完了以后释放锁
* 结果都是分组输出的,但是哪个线程先输出是随机的
*/
public class MyService {
private Lock lock=new ReentrantLock();
public void testMethod(){
lock.lock();
for(int i=0;i<2;i++){
System.out.println(Thread.currentThread().getName()+": "+i);
}
lock.unlock();
}
}
MyThread.javapublic class MyThread extends Thread{
private MyService myservice;
public MyThread(MyService myservice) {
super();
this.myservice=myservice;
}
public void run(){
myservice.testMethod();
}
}
Run.javapublic class Run {
public static void main(String[] args){
MyService myservice=new MyService();
MyThread a1=new MyThread(myservice);
MyThread a2=new MyThread(myservice);
MyThread a3=new MyThread(myservice);
MyThread a4=new MyThread(myservice);
MyThread a5=new MyThread(myservice);
a1.start();
a2.start();
a3.start();
a4.start();
a5.start();
}
}
结果如下:
ReentrantLock类的lock()相当于上锁,unlock()相当于解锁。
二、Condition对象来实现等待/通知
Condition对象的await()方法相当于synchronized的wait()方法,signal()方法相当于notify()方法,singnAll()方法相当于notifyAll()方法。
Condition对象的一个好处是它可以选择性的通知,即选择性的唤醒部分线程,即在一个Lock对象里可以创建出多个Condition对象,synchronized相当于整个Lock对象只有一个单一的Condition对象。
注意的是Condition对象的signal()方法也不是立即释放锁,需要执行完此线程的同步代码块里的方法后才能释放搜,在调用await()和signal()方法之前都需要先调用lock.lock()来获得同步监视器。
MyService.java
/*
* 使用多个Condition对象唤醒指定的线程。
*/
public class MyService {
private Lock lock=new ReentrantLock();
private Condition conditionA=lock.newCondition();
private Condition conditionB=lock.newCondition();
public void waitA(){
try{
lock.lock();
System.out.println(Thread.currentThread().getName()+" begin waitA!");
conditionA.await();
System.out.println(Thread.currentThread().getName()+" end waitA!");
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void waitB(){
try{
lock.lock();
System.out.println(Thread.currentThread().getName()+" begin waitB!");
conditionB.await();
System.out.println(Thread.currentThread().getName()+" end waitB!");
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void signalA(){
try{
lock.lock();
System.out.println("A"+" begin signal!");
conditionA.signalAll();
System.out.println("A"+" end signal!");
}finally{
lock.unlock();
}
}
public void signalB() {
try{
lock.lock();
System.out.println("B"+" begin signal!");
conditionB.signalAll();
System.out.println("B"+" end signal!");
}finally{
lock.unlock();
}
}
}
ThreadA.javapublic class ThreadA extends Thread{
private MyService myservice;
public ThreadA(MyService myservice) {
super();
this.myservice=myservice;
}
public void run(){
myservice.waitA();
}
}
ThreadB.javapublic class ThreadB extends Thread{
private MyService myservice;
public ThreadB(MyService myservice) {
super();
this.myservice=myservice;
}
public void run(){
myservice.waitB();
}
}
Run.javapublic class Run {
public static void main(String[] args) throws InterruptedException {
MyService myservice=new MyService();
ThreadA a=new ThreadA(myservice);
a.setName("A");
a.start();
ThreadB b=new ThreadB(myservice);
b.setName("B");
b.start();
Thread.sleep(3000);
myservice.signalA();
//myservice.signalB();
}
}
结果如下:
上述Run方法中只唤醒了A线程,因此程序还在运行,B 线程还在等待。修改Run方法,把唤醒线程B 的代码加上,结果如下:
等待/通知模式的案例当然少不了生产者/消费者模式的实现,主要注意消费者/生产者模式的多对多的交替打印,因为容易出现假死,注意两个点,一个是注意while而不同if,另一个是signalAll()而不是singnal()。
三、公平锁与非公平锁
公平锁表示线程获取锁的顺序是按照线程的加锁顺序来分配,即先进先出顺序;而非公平锁是一种获取锁的抢占机制,是随机获得锁的。
四、一些方法的介绍
1.getHoldCount()
查询当前线程保持此锁定的个数,即调用lock的次数。
2.getQueueLength()
返回正等待获取此锁定的线程数。比如有3个线程,其中一个线程执行了await,则此方法返回2,说明有2个线程正在同时等待lock的释放。
3.getWaitQueueLength()
返回等待与此锁定相关的给定条件Condition的线程估计数,比如有3个线程,每个线程都执行了同一个的condition对象的await方法,则调用此方法返回值是3。
4.hasQueuedThread(Thread thread)
查询指定线程是否正在等待获取此锁定。
5.hasQueuedThreads()
查询是否有线程正在等待获取此锁定。
6.hasWaiters()
查询是否有线程正在等待与此锁定相关的condition条件
7.isFair()
返回是否是公平锁
8.isHeldByCurrentThread()
查询当前线程是否保持此锁定。
9.isLocked()
查询此锁定是否由任意线程保持。
10.lockInterruptibly()
lock.lockInterruptibly :如果当前线程未被中断,则获取此锁定,如果已经被中断,则出现异常。
lock.lock :当前线程被中断爷不影响程序的进行。
11.awaitUninterruptibly()
Service.java
/*
* condition.await() 当前线程被中断时,就会进入异常
* condition.awaitUninterruptibly() 当前线程被中断时,不会出现异常
*/
public class Service {
private ReentrantLock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
public void testMethod(){
try{
lock.lock();
System.out.println("wait begin!");
//condition.await();
condition.awaitUninterruptibly();
System.out.println("wait end!");
}finally{
lock.unlock();
}
}
}
Run.javapublic class Run {
public static void main(String[] args) throws InterruptedException{
final Service service=new Service();
Runnable t1=new Runnable() {
@Override
public void run() {
service.testMethod();
}
};
Thread thread=new Thread(t1);
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
}
12.awaitUntil()
Service1.java
/*
* condition.awaitUntil(calendar.getTime()) 超过这个时间,线程会自动的被唤醒
* 同样在这个时间内,也可以被其他线程唤醒
*/
public class Service1 {
private ReentrantLock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
public void waitMethod(){
try{
Calendar calendar=Calendar.getInstance();
calendar.add(Calendar.SECOND,10);
lock.lock();
System.out.println("wait begin!");
condition.awaitUntil(calendar.getTime());
System.out.println("wait end!");
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void notifyMethod(){
try{
Calendar calendar=Calendar.getInstance();
calendar.add(Calendar.SECOND, 10);
lock.lock();
System.out.println("notify begin!");
condition.signalAll();
System.out.println("notify end!");
}finally{
lock.unlock();
}
}
}
Run1.javapublic class Run1 {
public static void main(String[] args){
final Service1 service=new Service1();
Runnable t1=new Runnable() {
@Override
public void run() {
service.waitMethod();
}
};
Runnable t2=new Runnable() {
@Override
public void run() {
service.notifyMethod();
}
};
Thread thread=new Thread(t1);
thread.start();
Thread thread1=new Thread(t2);
thread1.start();
}
}
13.使用Condition对象实现顺序执行
Run2.java
/*
* 使用Condition对象可以对线程执行的业务进行排序规划
*/
public class Run2 {
volatile private static int flag=1;
private static ReentrantLock lock=new ReentrantLock();
final private static Condition conditionA=lock.newCondition();
final private static Condition conditionB=lock.newCondition();
final private static Condition conditionC=lock.newCondition();
public static void main(String[] args){
Thread threadA=new Thread(){
public void run(){
try{
lock.lock();
while(flag!=1){
conditionA.await();
}
for(int i=0;i<3;i++){
System.out.println("ThreadA "+i);
}
flag=2;
conditionB.signalAll();
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
};
Thread threadB=new Thread(){
public void run(){
try{
lock.lock();
while(flag!=2){
conditionB.await();
}
for(int i=0;i<3;i++){
System.out.println("ThreadB "+i);
}
flag=3;
conditionC.signalAll();
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
};
Thread threadC=new Thread(){
public void run(){
try{
lock.lock();
while(flag!=3){
conditionC.await();
}
for(int i=0;i<3;i++){
System.out.println("ThreadC "+i);
}
flag=1;
conditionA.signalAll();
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
};
Thread[] aArray=new Thread[5];
Thread[] bArray=new Thread[5];
Thread[] cArray=new Thread[5];
for(int i=0;i<5;i++){
aArray[i]=new Thread(threadA);
bArray[i]=new Thread(threadB);
cArray[i]=new Thread(threadC);
aArray[i].start();
bArray[i].start();
cArray[i].start();
}
}
}