涉及AbstractQueuedSynchronizer使用-CountDownLatch等

AbstractQueuedSynchronizer是一个在并发开发中常用的抽象类,但是可能很多童鞋没有注意到因为我们间接用的都是它的子类;

如下这些(是不是很眼熟):

java.util.concurrent.CountDownLatch

java.util.concurrent.locks.ReentrantLock

java.util.concurrent.FutureTask

java.util.concurrent.Semaphore

这些类的作者是鼎鼎有名的Doug Lea,JAVA集合框架中随处可以看到他的身影, 佩服!

这些类用到了AbstractQueuedSynchronizer的子类(作为以上四个类的内部类Sync),这里涉及涉及模式中的“适配器模式”,大神的代码里处处有各种设计模式的身影,看源码再集合各种设计模式的讲解是一个很好的学习方法;

 

重点:

需要注意的是AbstractQueuedSynchronizer实现锁的机制是通过维护原子变量state的状态来完成的,跟synchronized实现的机制不一样;

 

 

可参看连接 http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html

CountDownLatch

场景

这是一个在多线程开发中控制线程执行流程的,比如在程序中开启4个线程来处理用户上传的若干图片,等这四个图片处理线程都处理完毕后要返回给用户处理的结果信息,就可以用到CountDownLatch;

接下来我们看看它的用法,直接上代码

 

如果不用CountDownLatch

public class Pic {
	private String picInfo;

	public Pic(String picInfo) {
		this.picInfo = picInfo;
	}

	public void doPic() {
		new doPicThread().start();
		new doPicThread().start();
		new doPicThread().start();
		new doPicThread().start();
		System.out.println("我处理完了");
	}

	class doPicThread extends Thread {

		@Override
		public void run() {
			System.out.println(Thread.currentThread().getName()+" 我拿到了:"+picInfo);
		}
	}
	
	public static void main(String[] args) {
		new Pic("iiii").doPic();
	}
}

 

结果

可以看到这样写的话是很有问题的,虽然代码中“我处理完了”是在启动所有线程后,但是线程的执行顺序由操作系统来选择;

比较好的处理方案就是引进CountDownLatch类来进行处理,在所有子线程没有处理前让主线程进入等待状态,等所有子线程执行

完了后才继续执行相关逻辑;

 

代码改进

public class Pic {
	private String picInfo;
	private CountDownLatch latch = new CountDownLatch(4);//有多少个线程就实例化几个Latch

	public Pic(String picInfo) {
		this.picInfo = picInfo;
	}

	public void doPic() {
		new doPicThread(latch).start();//需要把相同的latch传入到线程类中
		new doPicThread(latch).start();
		new doPicThread(latch).start();
		new doPicThread(latch).start();
		try {
			latch.await();//主线程调用latch的await方法,这个方法会让当前线程中止往下执行,直到每个线程中都执行完countDown()方法
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("我处理完了");
	}

	class doPicThread extends Thread {

		private CountDownLatch l;

		public doPicThread(CountDownLatch l) {
			this.l = l;
		}

		public void setL(CountDownLatch l) {
			this.l = l;
		}

		@Override
		public void run() {
			try {
				System.out.println(Thread.currentThread().getName() + " 我拿到了:"
						+ picInfo);
			} catch (Exception e) {
				// TODO: handle exception
			} finally {
				l.countDown();//注意这里countDown极力推荐写在finally中,因为如果由于某种异常这个方法没有执行那么主线程就要一直等待下去了
			}
		}
	}

	public static void main(String[] args) {
		new Pic("iiii").doPic();
	}
}

结果

原理

简单理解:在我们实例化CountDownLatch对象A的时候,需要传入一个int类型的参数a,这个参数用于设置AbstractQueuedSynchronizer类的state属性值(共有变量方式实现锁机制),当每个线程中调用A对象的countDown方法时这个值就会减少一,直至减少到0则处于interupt状态的主线程就会被唤醒。

 

 

ReentrantLock

场景

需要保证不同线程有序的执行一段代码(跟synchronized功能类似)即可使用ReentrantLock(可重入锁);

原理

以下是我的理解(看了几天源码也翻了很多资料,发现这个类代码简介但难懂):

1、调用ReentrantLock实例的lock方法时,当前线程就会去获得锁(尝试通过CAS算法修改内部类Sync父类AQS的state属性值由0边为1),如果修改成功即表示当前线程获得了锁,设置当前线程为拥有者

2、如果通过CAS算法没有修改成功,则当前线程通过自旋方式(循环)继续来获得锁(修改原子变量state的值)

3、。。。。。。

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,您想了解关于Java并发编程中的CountDownLatch吗? CountDownLatch是Java并发编程中非常重要的一个工具类,它可以让某个线程等待其他线程完成后再继续执行。它的作用类似于倒计时门闩,当count值减为0时,所有等待者都会被唤醒。 CountDownLatch是一次性的,也就是说,它的计数器只能使用一次。如果需要重复使用,可以考虑使用CyclicBarrier。 在使用CountDownLatch时,我们需要先创建一个CountDownLatch对象,并指定计数器的初始值。然后在需要等待的线程中调用await()方法进行等待,同时在其他线程中调用countDown()方法进行计数器的减1操作。 举个例子,假设我们有一个需求:主线程需要等待两个子线程完成后再执行。那么可以这样编写代码: ```java import java.util.concurrent.CountDownLatch; public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(2); new Thread(() -> { System.out.println(Thread.currentThread().getName() + "执行开始"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "执行完毕"); countDownLatch.countDown(); }, "线程1").start(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + "执行开始"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "执行完毕"); countDownLatch.countDown(); }, "线程2").start(); System.out.println(Thread.currentThread().getName() + "等待子线程执行完毕"); countDownLatch.await(); System.out.println(Thread.currentThread().getName() + "所有子线程执行完毕,继续执行主线程"); } } ``` 在上面的例子中,我们首先创建了一个计数器初始值为2的CountDownLatch对象,然后创建了两个线程分别进行一些操作,并在操作结束后调用countDown()方法进行计数器减1操作。在主线程中,我们调用await()方法进行等待,直到计数器减为0时,主线程才会继续执行。 希望能够对您有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值