并发编程10 多线程编程 及 jdk多线程工具类

多线程编程

  • 线程的常见操作。创建,运行,暂停,销毁,让步,停止,中断信号,等待,唤醒等
  • 多线程数据竞争问题,锁:sync关键字,Lock
  • 多线程“流程控制”,同步点(如Semaphore)
  • 多线程数据交换与共享

种类

  • Semaphore:信号量
  • CountDownLatch:“倒计数器”
  • CyclicBarrier:循环屏障,篱栅
  • Phaser:阶段转移器
  • Exchanger:交换器

Semaphore

作用

Semaphore:信号量,提供了资源数量的并发访问控制。
ReentrantLock可重入锁使得资源仅能被一个线程访问,Semaphore可以允许不超过指定数量的线程共同访问。

API使用

	// 一开始有5份共享资源。第二个参数表示是否是公平
	Semaphore myResources = new Semaphore(5, true);
	// 工作线程每获取一份资源,就在该对象上记下来
	// 在获取的时候是按照公平的方式还是非公平的方式,就要看上一行代码的第二个参数了。
	// 一般非公平抢占效率较高。
	// 超过限制数量5则会阻塞,直到获取到资源
	myResources.acquire();
	// 工作线程每归还一份资源,就在该对象上记下来
	// 此时资源可以被其他线程使用
	myResources.release();
	/*
		释放指定数目的许可,并将它们归还给信标。
		可用许可数加上该指定数目。
		如果线程需要获取N个许可,在有N个许可可用之前,该线程阻塞。
		案例:
		大学生到自习室抢座,写作业:
		如果线程获取了N个许可,还有可用的许可,则依次将这些许可赋予等待获取许可的其他线程。
	*/
	semaphore.release(2);
	/*
		从信标获取指定数目的许可。如果可用许可数目不够,则线程阻塞,直到被中断。
		该方法效果与循环相同,
		for (int i = 0; i < permits; i++) acquire();
		只不过该方法是原子操作。
		如果可用许可数不够,则当前线程阻塞,直到:(二选一)
		1. 如果其他线程释放了许可,并且可用的许可数满足当前线程的请求数字;
		2. 其他线程中断了当前线程。
		permits – 要获取的许可数
	*/
	semaphore.acquire(3);

实现

类图

在这里插入图片描述
通过类图可以发现其结构和ReentrantLock及其相似

CountDownLatch

作用

计数器,控制线程的执行顺序。一般用于主线程等待子线程执行完毕后继续执行。

API使用

//5是倒计数值
CountDownLatch latch = new CountDownLatch(5);
//子线程执行完毕后调用countDown()方法,计数减1
latch.countDown();
//await()方法阻塞当前线程,直到计数器归零,唤醒本线程,继续执行
latch.await();
//其他业务代码

实现

类图

在这里插入图片描述
同样基于AQS实现

CyclicBarrier

作用

和CountDownLatch类似但是更强大。CountDownLatch可以使多个线程同步一次,CyclicBarrier可以同步多次。
在这里插入图片描述

API使用

CyclicBarrier barrier = new CyclicBarrier(5);
//子线程业务代码块1
//子线程同步点1,阻塞本线程,直到计数器归零后,才唤起本线程
barrier.await();
//子线程业务代码块2
//子线程同步点2,阻塞本线程,直到计数器归零后,才唤起本线程
barrier.await();
//...

原理

  • 使用倒计数器
  • 使用ReentrantLock加锁,Condition trip 实现线程的阻塞和唤醒
public class CyclicBarrier {
	// 可重入锁
	private final ReentrantLock lock = new ReentrantLock();
	// 用于线程之间相互唤醒
	private final Condition trip = lock.newCondition();
	// 线程总数
	private final int parties;
	// 计数器
	private int count;
	// 世代,即同步点
	private Generation generation = new Generation();
	// ...

	// 构造方法
	public CyclicBarrier(int parties, Runnable barrierAction) {
		if (parties <= 0) throw new IllegalArgumentException();
			// 参与方数量
			this.parties = parties;
			this.count = parties;
			// 当所有线程被唤醒时,执行barrierCommand表示的Runnable。
			this.barrierCommand = barrierAction;
		}
	}
	
	public CyclicBarrier(int parties) {
        this(parties, null);
    }
	
	public int await() throws InterruptedException, BrokenBarrierException {
		try {
			return dowait(false, 0L);
		} catch (TimeoutException toe) {
			throw new Error(toe); // cannot happen
		}
	}
	
 	private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException, TimeoutException {
		final ReentrantLock lock = this.lock;
		lock.lock();
		try {
			final Generation g = generation;
			// 首先检查屏障是否破损,即其他线程调用了breakBarrier,例如:
			//   1. 其他线程响应中断
			//   2. barrierAction执行异常
			//   3. 执行超时
			// 若破损则抛出异常BrokenBarrierException
			if (g.broken)
				throw new BrokenBarrierException();
			// 响应中断,如:接到其他线程的中断信号
			//   本线程抛出中断异常,其他阻塞的线程被唤醒抛出屏障破损
			if (Thread.interrupted()) {
				// 打破屏障,本世代重新倒计数,唤醒所有阻塞的线程
				breakBarrier();
				// 抛出中断异常
				throw new InterruptedException();
			} 
			// 每个线程调用一次await(),count都要减1
			int index = --count;
			// 当count减到0的时候,此线程唤醒其他所有线程
			if (index == 0) { // tripped
				boolean ranAction = false;
				try {
					final Runnable command = barrierCommand;
					if (command != null)
						command.run();
					ranAction = true;
					//开启下个世代,唤起所有线程,重新倒计数
					nextGeneration();
					return 0;
				} finally {
					if (!ranAction)
						breakBarrier();
				}
			} 
			
			// 循环执行,直到同步点,屏障破损,中断信号或者超时
			// loop until tripped, broken, interrupted, or timed out
			for (;;) {
				try {
					if (!timed)
						trip.await();
					else if (nanos > 0L)
						nanos = trip.awaitNanos(nanos);
				} catch (InterruptedException ie) {
					if (g == generation && ! g.broken) {
						breakBarrier();
						throw ie;
					} else {
						// We're about to finish waiting even if we had not
						// been interrupted, so this interrupt is deemed to
						// "belong" to subsequent execution.
						Thread.currentThread().interrupt();
					}
				} 
				if (g.broken)
					throw new BrokenBarrierException();
				if (g != generation)
					return index;
				if (timed && nanos <= 0L) {
					breakBarrier();
					throw new TimeoutException();
				}
			}
		} finally {
			lock.unlock();
		}
	} 

	private void breakBarrier() {
		generation.broken = true;
		count = parties;
		trip.signalAll();
	} 
	
	private void nextGeneration() {
		// signal completion of last generation
		trip.signalAll();
		// set up next generation
		count = parties;
		generation = new Generation();
	}

Phaser

作用

功能比CyclicBarrier和CountDownLatch更加强大

API使用

Phaser phaser = new Phaser(5);

// 替代CountDownLatch
// arrive:标记为已到达,即计数器减一
phaser.arrive();
// 同步点等待,阻塞,直到计数器归零
phaser.awaitAdvance(phaser.getPhase());

// 替代CyclicBarrier
// 到达同步点并等待,即计数器减一并阻塞,直到计数器归零
phaser.arriveAndAwaitAdvance();

// 新特性之一:动态调整线程个数
register() // 注册一个
bulkRegister(int parties) // 注册多个
arriveAndDeregister() // 解除注册

// 新特性之二:层次Phaser
Phaser root = new Phaser(2);
Phaser c1 = new Phaser(root, 3);
Phaser c2 = new Phaser(root, 2);
Phaser c3 = new Phaser(c1, 0);
// 形成了如下图的树状Phaser
/* 
	本来root有两个参与者,然后为其加入了两个子Phaser(c1,c2),每个子Phaser会算作1个参与者,root的参与者就变成2+2=4个。
	c1本来有3个参与者,为其加入了一个子Phaser c3,参与者数量变成3+1=4个。
	c3的参与者初始为0,后续可以通过调用register()方法加入。
	对于树状Phaser上的每个节点来说,可以当作一个独立的Phaser来看待,其运作机制和一个单独的Phaser是一样的。
	父Phaser并不用感知子Phaser的存在,当子Phaser中注册的参与者数量大于0时,会把自己向父节点注册。
	当子Phaser中注册的参与者数量等于0时,会自动向父节点解除注册。父Phaser把子Phaser当作一个正常参与的线程就即可。
*/

在这里插入图片描述

原理

Phaser没有基于AQS来实现,但具备AQS的核心特性:state变量、CAS操作、阻塞队列。先从state变量说起。

Exchanger

作用

用于两个线程之间交换数据

API使用

Exchanger<String> exchanger = new Exchanger<>();
// 如果没有其他线程调用exchange,线程阻塞,直到有其他线程调用exchange为止,
String otherData = exchanger.exchange("交换数据1");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值