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.java
public class MyThread extends Thread{
private MyService myservice;
	public MyThread(MyService myservice) {
		super();
		this.myservice=myservice;
	}
public void run(){
	myservice.testMethod();
}
}
Run.java
public 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.java
public class ThreadA extends Thread{
private MyService myservice;
	public ThreadA(MyService myservice) {
		super();
		this.myservice=myservice;
	}
public void run(){
	myservice.waitA();
}
}
ThreadB.java
public class ThreadB extends Thread{
private MyService myservice;
	public ThreadB(MyService myservice) {
		super();
		this.myservice=myservice;
	}
public void run(){
	myservice.waitB();
}
}
Run.java
public 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.java
public 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.java
public 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();
    }
}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值