【多线程】ReentrantLock重入锁

一、Lock接口

       在java多线程中,可以使用synchronized关键字来实现线程之间同步互斥、但在JDK1.5之后并发包中新增加了Lock接口(以及相关实现类)用来实现锁功能,它提供了与synchronized关键字类似的同步功能,只是在使用时需要显示地获取和释放锁,虽然它缺少了隐式获取释放锁的便捷性,但是却拥有了锁获取与释放的可操作性、可中断的获取或及超时获取锁等多种synchronized关键字锁不具备的同步特性。

       synchronized关键字会隐式获取锁,但是它把锁的获取和释放固化了,也就是先获取再使用。不可否认的是这种方式简化了同步的管理,可以扩展性没有显示的锁获取和释放来的好。

       1、Lock接口提供的synchronized关键字不具备的主要特性

       

       2、Lock的API

      



 

二、使用ReentrantLock实现同步

     

public class MyService {
	private Lock lock=new ReentrantLock();
	public void testMethod(){
		//调用ReentrantLock对象的lock方法获取锁,调用unlock方法释放锁
		lock.lock();
		for (int i = 0; i < 5; i++) {
			System.out.println("ThreadName="+Thread.currentThread().getName()+(" "+(i+1)));
		}
		lock.unlock();
	}

}
    
public class MyThread extends Thread {
	private MyService service;
	public MyThread(MyService service){
		super();
		this.service=service;
	}
	@Override
	public void run(){
		service.testMethod();
	}

}
    
public class Run {
	public static void main(String[] args) {
		MyService service=new MyService();
		MyThread a1=new MyThread(service);
		a1.setName("a1");
		MyThread a2=new MyThread(service);
		a2.setName("a2");
		MyThread a3=new MyThread(service);
		a3.setName("a3");
		MyThread a4=new MyThread(service);
		a4.setName("a4");
		MyThread a5=new MyThread(service);
		a5.setName("a5");
		
		a1.start();
		a2.start();
		a3.start();
		a4.start();
		a5.start();
	}

}
     从运行的结果来看,当前线程打印完毕之后将锁进行释放,其他线程才可以继续打印。线程打印的数据是分组打印,因为当前线程已经持有锁,但线程之间的顺序是随机的。

三、使用Condition实现等待/通知

       关键字synchronized与wait()和notify()/notifyAll()方法相结合可以实现等待/通知模式。类ReentrantLock也可以实现同样的功能。但需要借助于Condition对象。Condition类是在JDK5中出现的技术,使用它有更好的灵活性,比如可以实现多路通知功能,也就是在一个Lock对象里面可以创建多个Condition(对象监视器)实例,线程对象可以注册在指定的condtion中,从而有选择进行线程通知线程调度上更灵活。

       在使用notify方法进行通知时,被通知的线程是JVM随机选择的。但ReentrantLock+Condition可以实现“选择性通知”。

       synchronized就相当于整个Lock对象中只有一个单一的Conditon对象,所有线程都注册在它一个对象的身上。线程开始notifyAll时,需要通知所有Wait状态的线程,没有选择权。

1、使用Condition实现等待/通知错误用法与解决

public class MyService {
	private ReentrantLock lock=new ReentrantLock();
	private Condition condition=lock.newCondition();
	public void waitMethod(){
		try {
			//lock.lock();
			System.out.println("A");
			condition.await();
			System.out.println("B");
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			lock.unlock();
			System.out.println("锁被释放了");
		}
	}

}
public class MyThreadA extends Thread {
	private MyService myService;
	public MyThreadA(MyService myService){
		this.myService=myService;
	}
	@Override
	public void run(){
		myService.waitMethod();
	}
}
public class Run {

	public static void main(String[] args) {
		MyService myService =new MyService();
		MyThreadA a=new MyThreadA(myService);
		a.start();
		//在控制台只打印一个字母A,原因是调用了Condition对象的await方法,使当前执行任务的线程进入等待WAITING状态
	}

}
    
        报错的异常信息是监视器出错,解决的办法是必须在condition.await方法之前调用lock.lock()代码获得同步监视器。将

lock.lock();的代码解注。最终在控制台中只打印了一个字母A,原因是调用了Condition对象的await方法。使当前线程进入了wait状态。

2、正确使用Condition实现等待/通知

      

public class MyService {
	private Lock lock=new ReentrantLock();
	public Condition condition=lock.newCondition();
	public void await(){
		try {
			lock.lock();
			System.out.println(" await时间为  "+System.currentTimeMillis());
			condition.await();
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}
	public void signal(){
		try {
			lock.lock();
			System.out.println("signal时间为"+System.currentTimeMillis());
			condition.signal();
		}finally{
			lock.unlock();
		}
	}

}
     
public class ThreadA extends Thread {
	private MyService service;
	public ThreadA(MyService service){
		super();
		this.service=service;
	}
	@Override
	public void run(){
		service.await();
	}
	public static void main(String[] args) throws InterruptedException {
		MyService service=new MyService();
		ThreadA a=new ThreadA(service);
		a.start();
		Thread.sleep(3000);
		service.signal();
	}

}
     
public class Run {

	public static void main(String[] args) throws InterruptedException {
		MyService service=new MyService();
		ThreadA a = new ThreadA(service);
		a.start();
		Thread.sleep(3000);
		service.signal();
	}

}

     程序运行结果如下图所示,成功实现了等待/通知模式.

     

       ①  Object类中的wait方法相当于Condition类中的await方法.

       ②  Object类中的wait(long timeout)方法相当于Condition类中的await(long time,TimeUnit unit)

       ③  Object类中的notify方法相当于Condition类中的signal方法.

       ④  Object类中的notifyAll方法相当于Condition类中的signalAll方法

 3、使用多个Condition实现通知部分线程:

     MyService:

public class MyService {
	private Lock lock=new ReentrantLock();
	//两个condition对象
	public Condition conditionA=lock.newCondition();
	public Condition conditionB=lock.newCondition();
	public void awaitA(){
		try {
			lock.lock();
			System.out.println("begin awaitA时间为" +System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
			conditionA.await();
			System.out.println("  end awaitA时间为" +System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());	
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}
	
	public void awaitB(){
		try {
			lock.lock();
			System.out.println("begin awaitB时间为" +System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
			conditionB.await();
			System.out.println("  end awaitB时间为" +System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());	
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			lock.unlock();
		}	
	}
	
	public void signalAll_A(){
		try {
			lock.lock();
			System.out.println("  signalAll_A 时间为"+System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
			conditionA.signalAll();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}
	
	public void signalAll_B(){
		try {
			lock.lock();
			System.out.println("  signalAll_B 时间为"+System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
			conditionA.signalAll();
		} catch (Exception e) {
			lock.unlock();
		}finally{
			lock.unlock();
		}
	}
	

}
     
public class ThreadA extends Thread {
	private MyService service;
	public ThreadA(MyService service){
		super();
		this.service=service;
	}
	@Override
	public void run(){
		service.awaitA();
	}
}
    
public class ThreadB extends Thread {
	private MyService service;
	public ThreadB(MyService service){
		super();
		this.service=service;
	}
	@Override
	public void run(){
		service.awaitB();
	}
}
      测试类:

     

public class Run {

	public static void main(String[] args) throws InterruptedException {
		MyService service=new MyService();
		ThreadA a=new ThreadA(service);
		a.setName("A");
		a.start();
		ThreadB b=new ThreadB(service);
		b.setName("B");
		b.start();
		ThreadA.sleep(3000);
		service.signalAll_A();
		//通过此实验可以得知,使用ReentrantLock对象可以唤醒指定种类的线程,这是控制部分线程行为的方便方式
	}

}
     

         程序运行后,只有线程A被唤醒了,这说明使用ReentrantLock+condition对象可以唤醒指定部分的线程,可以先对线程分组,然后再唤醒指定组中的线程。

四、公平锁与非公平锁

        锁Lock分为"公平锁 "、“非公平锁”,公平锁表示线程获得锁的顺序是按照线程加锁的顺序来分配的,即FIFO先进先出顺序。而非公平锁就是一种获取锁的抢战机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果也就是不公平的了。

        

public class Service {
	private ReentrantLock lock;
	public Service(boolean isFair){
		super();
		lock=new ReentrantLock(isFair);
	}
	public void serviceMethod(){
		try {
			lock.lock();
			System.out.println("ThreadName= "+Thread.currentThread().getName()+"获得锁定");
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}

}
     公平锁:

      

public class RunFair {
	/**
	 * 打印的结果是基本呈有序的,这就是公平锁的特点
	 * @param args
	 */
	public static void main(String[] args) {
		final Service service=new Service(true);
		Runnable runnable=new Runnable() {
			
			@Override
			public void run() {
				System.out.println("☆线程"+Thread.currentThread().getName()+"运行了");
				service.serviceMethod();
				
			}
		};
		Thread[] threadArray=new Thread[10];
		for (int i = 0; i < 10; i++) {
			threadArray[i]=new Thread(runnable);
		}
		for (int i = 0; i < 10; i++) {
			threadArray[i].start();
		}
		
	}
}
     非公平锁:

      

public static void main(String[] args) {
		/**
		 * 非公平锁的运行结果基本上是乱序的,说明先start启动的线程不代表先获得锁
		 */
		final Service service=new Service(false);
		Runnable runnable=new Runnable() {
			
			@Override
			public void run() {
				System.out.println("☆线程"+Thread.currentThread().getName()+"运行了");
				service.serviceMethod();
			}
		};
		Thread[] threadArray=new Thread[10];
		for (int i = 0; i < 10; i++) {
			threadArray[i]=new Thread(runnable);
		}
		for (int i = 0; i < 10; i++) {
			threadArray[i].start();
		}
	}

}






      

        

           


       



        




     

        

        

       


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值