java.util.concurrent 并发工具包(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qinshi965273101/article/details/81981688

该包让java并发编程更轻松。

一、Blocking Queue

1、BlockingQueue介绍

阻塞式队列,通常用于一个线程生产对象,而另外一个线程消费这些对象的场景。

操作BlockingQueue时,当操作不能立即执行,各个方法表现也不同,如下:

  抛异常 返回特定值 阻塞 超时
插入 add(o) offer(o) put(o) offer(o,timeout,timeunit)
移除 remove(o) poll(o) task(o) poll(timeout,timeunit)
检查 element(o) peek(o)    

例如,当BlockingQueue中没有数据时,调用task()拿数据则会产生阻塞,直到有数据可以拿。 

2、实现BlockingQueue接口的具体类

类名 类中文名 特点
ArrayBlockingQueue 数组阻塞队列 基于数组实现,有大小限制,且定义好后无法修改大小。
DelayQueue 延迟队列 无大小限制
LinkedBlockingDeque 链阻塞队列 基于链式结构实现,无大小限制
PriorityBlockingQueue 有优先级的阻塞队列  
SynchronousQueue 同步队列  

3、双端队列

二、并发Map,ConcurrentMap

ConcurrentHashMap

具备 HashTable的线程安全,也具备 HashMap的并发性

在对ConcurrentHashMap 进行读和写时,并不会锁住整个map,而是把真个ConcurrentHashMap分成多块,只对正在操作的那一块进行锁定。

三、闭锁 (递减锁) CountDownLatch

初始化时给定一个数量,每调用一次。countDown(),数量就减一。配合 await()方法使用

 await()方法的作用:阻塞,直到CountDownLatch的数量减为零,才会继续往下执行。

public static void main(String[] args) throws Exception {
	CountDownLatch latch = new CountDownLatch(3);
	latch.countDown();
	latch.countDown();
	latch.countDown();
	//阻塞,直到3次机会用完
	latch.await();
	System.out.println("三次减完了!");
}

四、栅栏 CyclicBarrier

初始化时给定一个数量。

当调用 await() 时产生阻塞,直到调用 await() 方法的次数达到这个数量时,所有调用await()的方法处,都会继续往下执行。

下面的例子:多个线程全部到达栅栏后,各个线程才会继续执行。

package day01Current;

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {
	public static void main(String[] args) throws Exception {
		CyclicBarrier cb = new CyclicBarrier(2);
		
		new Thread(new T1(cb)).start();
		new Thread(new T2(cb)).start();

	}
}

class T1 implements Runnable {
	private CyclicBarrier cb = null;
	public T1(CyclicBarrier cb) {
		this.cb = cb;
	}
	
	public void run() {
		
		try {
			Thread.sleep(2000);
			System.out.println("T1到达栅栏!");
			cb.await();
			System.out.println("T1出了栅栏!");
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
}

class T2 implements Runnable {
	private CyclicBarrier cb = null;
	public T2(CyclicBarrier cb) {
		this.cb = cb;
	}
	
	public void run() {
		
		try {
			Thread.sleep(5000);
			System.out.println("T2到达栅栏!");
			cb.await();
			System.out.println("T2出了栅栏!");
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
}

五、交换机 Exchanger

创建一个交换机实例,两个线程可以通过这个交换机实例互相交换对象。

交换对象是通过 Exchanger实例的 exchange 方法,该方法会把对象交给另一个线程的同时,返回从另一个线程获取的对象。

代码示例如下:

package day01Current;

import java.util.concurrent.Exchanger;

public class ExchangerDemo {
	
	public static void main(String[] args) throws Exception {
		Exchanger<String> exc = new Exchanger<String>();
		
		new Thread(new T1(exc)).start();
		new Thread(new T2(exc)).start();

	}
}

class T1 implements Runnable {
	private Exchanger<String> exc = null;
	public T1(Exchanger<String> exc) {
		this.exc = exc;
	}
	
	public void run() {
		
		try {
			String obj = exc.exchange("线程T1生成的对象");
			System.out.println("T1交换得到的对象为:  " + obj);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
}

class T2 implements Runnable {
	private Exchanger<String> exc = null;
	public T2(Exchanger<String> exc) {
		this.exc = exc;
	}
	
	public void run() {
		
		try {
			String obj = exc.exchange("线程T2生成的对象");
			System.out.println("T2交换得到的对象为:  " + obj);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
}

六、 信号量 Semaphore

该类是一个 计数信号量。

初始化时指定一个数量的 "许可" ,每调用一次 acquire() 方法,一个许可会被调用线程取走。每调用一次 release(),一个许可会被返还给信号量。如果没有 "许可" 可以取走,则 acquire() 会阻塞release()方法不会阻塞

release()方法类似于生产者,每调用一次release()方法,就会增加一个许可,且可以超出定义时的个数限制。所以acquire()和release()两个方法一定要配对使用


1. 保护一个重要(代码)部分,防止一次超过 N 个线程进入。

Semaphore semaphore = new Semaphore(1);
//1、获取执行许可
semaphore.acquire();
//2、执行被保护的代码块
//3、执行完后返还许可
semaphore.release();

2. 在两个线程之间发送信号

想将一个信号量用于在两个线程之间传送信号,通常应该用一个线程调用 acquire() 方法,而另一个线程调用 release() 方法。

这样可以对多个线程进行协调。

猜你喜欢

转载自blog.csdn.net/qinshi965273101/article/details/81981688